脚本化 field 示例

本小节展示了 Kibana 中一些常见场景下的 Lucene expressions 和 Painless 脚本化 field 示例。如上所述,这些示例以来自 Kibana 入门教程的数据集为基础,并且假定你使用的是 Elasticsearch 和 Kibana 5.1.1,因为在之前的版本中,某些类型的脚本化 field 中存在一些与过滤和排序相关的已知问题。

由于 Elasticsearch 5.0 默认启用 Lucene expressions 和 Painless,因此脚本化 fields 在大部分情况下应该都能够开箱即用。唯一例外的是那些需要对 fields 进行基于正则表达式的解析的脚本,这些脚本需要你在 elasticsearch.yml中设置下面的设置,为 Painless 打开正则表达式匹配:

script.painless.regex.enabled: true

对单个 field 执行计算
示例:由字节计算出千字节
语言:expressions
返回类型:数字

doc['bytes'].value / 1024

注意:切记 Kibana 脚本化 fields 一次只能处理一个单独的文档,因此无法在脚本化 fields 中进行时间序列运算。

返回数字的日期运算
示例:将日期解析成小时时间
语言:expressions
返回类型:数字
Lucene expressions 提供了大量开箱即用的 日期处理函数。但是,由于 Lucene expressions 只能返回数字值,因此我们必须使用 Painless 来返回基于字符串的星期值(如下所示)。

doc['@timestamp'].date.hourOfDay

注意:上面的脚本将返回 1-24

doc['@timestamp'].date.dayOfWeek

注意:上面的脚本将返回 1-7

合并两个字符串值
示例:合并源和目标或名字和姓氏
语言:painless
返回类型:字符串

doc['geo.dest.keyword'].value + ':' + doc['geo.src.keyword'].value

注意:由于脚本化 field 需要操作doc_values中的 field,因此我们上面使用的是 .keyword 版本的字符串。

引入逻辑运算
示例:为所有超过 10000 字节的文档返回标签“big download”
语言:painless
返回类型:字符串

if (doc['bytes'].value > 10000) { 
    return "big download";
}
return "";

注意:引入逻辑运算时,确保每个执行路径都具有良好定义的返回语句和良好定义的返回值(而非 null)。例如,在 Kibana 过滤器中使用上述脚本化 field 时,如果最后没有返回语句或者语句返回 null,都会出现编译错误。另外还请注意,Kibana 脚本化 field 中不支持将逻辑运算分解成函数。

返回子串
示例:返回 URL 中最后一个斜线后面的部分
语言:painless
返回类型:字符串

def path = doc['url.keyword'].value;
if (path != null) {
    int lastSlashIndex = path.lastIndexOf('/');
    if (lastSlashIndex > 0) {
    return path.substring(lastSlashIndex+1);
    }
}
return "";

注意:尽量避免使用正则表达式提取子串,因为 indexOf() 操作占用的资源更少,更不易出错。

使用正则表达式匹配字符串,并对匹配进行操作
示例:如果在 field“referer”中找到子串“error”,则返回字符串“error”,否则返回字符串“no error”。
语言:painless
返回类型:字符串

if (doc['referer.keyword'].value =~ /error/) { 
return "error"
} else {
return "no error"
}

注意:简化的正则表达式语法对基于正则表达式匹配的条件句有用。

匹配字符串并返回该匹配
示例:返回域,即 field “host”中最后一个点后面的字符串。
语言:painless
返回类型:字符串

def m = /^.*\.([a-z]+)$/.matcher(doc['host.keyword'].value);
if ( m.matches() ) {
   return m.group(1)
} else {
   return "no match"
}

注意:通过正则表达式 matcher() 函数定义对象,可以提取与正则表达式相匹配的字符组并将它们返回。

匹配数字并返回该匹配
示例:返回 IP 地址的第一个八位组(存储为字符串)并将它视为一个数字。
语言:painless
返回类型:数字

def m = /^([0-9]+)\..*$/.matcher(doc['clientip.keyword'].value);
if ( m.matches() ) {
   return Integer.parseInt(m.group(1))
} else {
   return 0
}

注意:在脚本中返回正确的数据类型是很重要的。正则表达式匹配返回的是字符串,即便匹配的是数字依然返回字符串,因此返回时应该显式地将它转换成整数。

返回字符串的日期运算
示例:将日期解析成星期值再解析成字符串
语言:painless
返回类型:字符串

LocalDateTime.ofInstant(Instant.ofEpochMilli(doc['@timestamp'].value), ZoneId.of('Z')).getDayOfWeek().getDisplayName(TextStyle.FULL, Locale.getDefault())

注意:由于 Painless 支持 Java 所有的原生类型,因此通过它可以获取与这些类型相关的原生函数,例如 LocalDateTime(),这在执行更加高级的日期运算时有用。