考虑要提取字段的大量日志数据。 为数据建立索引非常耗时,并且会占用大量磁盘空间,而你只想探索数据结构而无需预先提交 schema。

你知道你的日志数据包含你要提取的特定字段。 在这种情况下,我们要关注 @timestamp 和消息字段。 通过使用运行时字段(runtime fields),你可以定义脚本来计算这些字段在搜索时的值。

定义索引字段作为起点

你可以从一个简单的示例开始,将 @timestamp 和 message 字段作为索引字段添加到 my-index-000001 映射中。 为了保持灵活性,使用 wildcard 作为消息的字段类型:

PUT /my-index-000001/
{"mappings": {"properties": {"@timestamp": {"format": "strict_date_optional_time||epoch_second","type": "date"},"message": {"type": "wildcard"}}}
}

在上面,我们有意使用 wildcard 字段来定义 message。这样它非常节省存储空间,并且会提高写入文档的速度。

摄取一些数据

映射完要检索的字段后,将日志数据中的几条记录索引到 Elasticsearch 中。 以下请求使用 _bulk API 将原始日志数据索引到 my-index-000001。 你可以使用一个小样本来试验运行时字段,而不是索引所有日志数据。

最终文档不是有效的 Apache 日志格式,但我们可以在脚本中考虑到这种情况。

POST /my-index-000001/_bulk?refresh
{"index":{}}
{"timestamp":"2020-04-30T14:30:17-05:00","message":"40.135.0.0 - - [30/Apr/2020:14:30:17 -0500] \"GET /images/hm_bg.jpg HTTP/1.0\" 200 24736"}
{"index":{}}
{"timestamp":"2020-04-30T14:30:53-05:00","message":"232.0.0.0 - - [30/Apr/2020:14:30:53 -0500] \"GET /images/hm_bg.jpg HTTP/1.0\" 200 24736"}
{"index":{}}
{"timestamp":"2020-04-30T14:31:12-05:00","message":"26.1.0.0 - - [30/Apr/2020:14:31:12 -0500] \"GET /images/hm_bg.jpg HTTP/1.0\" 200 24736"}
{"index":{}}
{"timestamp":"2020-04-30T14:31:19-05:00","message":"247.37.0.0 - - [30/Apr/2020:14:31:19 -0500] \"GET /french/splash_inet.html HTTP/1.0\" 200 3781"}
{"index":{}}
{"timestamp":"2020-04-30T14:31:22-05:00","message":"247.37.0.0 - - [30/Apr/2020:14:31:22 -0500] \"GET /images/hm_nbg.jpg HTTP/1.0\" 304 0"}
{"index":{}}
{"timestamp":"2020-04-30T14:31:27-05:00","message":"252.0.0.0 - - [30/Apr/2020:14:31:27 -0500] \"GET /images/hm_bg.jpg HTTP/1.0\" 200 24736"}
{"index":{}}
{"timestamp":"2020-04-30T14:31:28-05:00","message":"not a valid apache log"}

此时,你可以查看 Elasticsearch 如何存储你的原始数据。

GET my-index-000001

该映射包含两个字段:@timestamp 和 message。

{"my-index-000001": {"aliases": {},"mappings": {"properties": {"@timestamp": {"type": "date","format": "strict_date_optional_time||epoch_second"},"message": {"type": "wildcard"},"timestamp": {"type": "date"}}},"settings": {"index": {"routing": {"allocation": {"include": {"_tier_preference": "data_content"}}},"number_of_shards": "1","provided_name": "my-index-000001","creation_date": "1672032735783","number_of_replicas": "1","uuid": "X1cBJOl3TFKd6v0oeTRlng","version": {"created": "8050399"}}}}
}

使用 grok 模式定义运行时字段

如果要检索包含 clientip 的结果,可以将该字段添加为映射中的运行时字段。 以下运行时脚本定义了一个 grok 模式,该模式从文档中的单个文本字段中提取结构化字段。 grok 模式就像支持可以重用的别名表达式的正则表达式。

该脚本匹配 %{COMMONAPACHELOG} 日志模式,该模式了解 Apache 日志的结构。 如果模式匹配 (clientip != null),脚本将发出匹配 IP 地址的值。 如果模式不匹配,脚本只会返回字段值而不会崩溃。

PUT my-index-000001/_mappings
{"runtime": {"http.client_ip": {"type": "ip","script": """String clientip=grok('%{COMMONAPACHELOG}').extract(doc["message"].value)?.clientip;if (clientip != null) emit(clientip); """}}
}

我们可以为已经存在的索引动态地添加一个新的字段。特别值得指出的是上面的 ?. 操作符。我们可以参阅链接来进一步阅读。它的意思是对一个 null 对象使用 ?. 操作符会返回 null,而不会使得脚本崩溃。上面的 if 检查,此条件可确保脚本不会崩溃,即使 message 的模式不匹配也是如此。

这样,我们可以针对索引进行搜索,比如:

GET my-index-000001/_search?filter_path=**.hits
{"query": {"match": {"http.client_ip": "40.135.0.0"}}
}

上面的 runtime 字段 http.client_ip 在查询时动态生成,并使得我们可以对它进行搜索:

{"hits": {"hits": [{"_index": "my-index-000001","_id": "Zn7rTIUBIjh__4nuBm2T","_score": 1,"_source": {"timestamp": "2020-04-30T14:30:17-05:00","message": """40.135.0.0 - - [30/Apr/2020:14:30:17 -0500] "GET /images/hm_bg.jpg HTTP/1.0" 200 24736"""}}]}
}

在上面,我们在 mapping 中定义 runtime fields。在实际的使用中,我们也可以在搜索的时候定义。你可以在搜索请求的上下文中定义相同的运行时字段。 运行时定义和脚本与之前在索引映射中定义的完全相同。 只需将该定义复制到 runtime_mappings 部分下的搜索请求中,并包含与运行时字段匹配的查询。 此查询返回的结果与你在索引映射中为 http.clientip 运行时字段定义搜索查询时返回的结果相同,但仅在此特定搜索的上下文中:

GET my-index-000001/_search?filter_path=**.hits
{"runtime_mappings": {"http.clientip": {"type": "ip","script": """String clientip=grok('%{COMMONAPACHELOG}').extract(doc["message"].value)?.clientip;if (clientip != null) emit(clientip);"""}},"query": {"match": {"http.clientip": "40.135.0.0"}},"fields" : ["http.clientip"]
}

上面的搜索返回结果:

{"hits": {"hits": [{"_index": "my-index-000001","_id": "Zn7rTIUBIjh__4nuBm2T","_score": 1,"_source": {"timestamp": "2020-04-30T14:30:17-05:00","message": """40.135.0.0 - - [30/Apr/2020:14:30:17 -0500] "GET /images/hm_bg.jpg HTTP/1.0" 200 24736"""},"fields": {"http.clientip": ["40.135.0.0"]}}]}
}

定义复合运行时字段

你还可以定义复合(composite)运行时字段以从单个脚本发出多个字段。 你可以定义一组类型化的子字段并发出值映射。 在搜索时,每个子字段在地图中检索与其名称关联的值。 这意味着你只需指定一次 grok 模式并可以返回多个值:

PUT my-index-000001/_mappings
{"runtime": {"http": {"type": "composite","script": "emit(grok(\"%{COMMONAPACHELOG}\").extract(doc[\"message\"].value))","fields": {"clientip": {"type": "ip"},"verb": {"type": "keyword"},"response": {"type": "long"}}}}
}

搜索一个特定的 IP 地址

使用 http.clientip 运行时字段,你可以定义一个简单的查询来运行对特定 IP 地址的搜索并返回所有相关字段。

GET my-index-000001/_search?filter_path=**.hits
{"query": {"match": {"http.clientip": "40.135.0.0"}},"fields" : ["*"]
}

上面的 API 返回以下结果。 因为 http 是复合运行时字段,所以响应包括字段下的每个子字段,包括任何与查询匹配的关联值。 无需提前构建数据结构,你就可以以有意义的方式搜索和探索数据,以试验并确定要索引的字段。

{"hits": {"hits": [{"_index": "my-index-000001","_id": "Zn7rTIUBIjh__4nuBm2T","_score": 1,"_source": {"timestamp": "2020-04-30T14:30:17-05:00","message": """40.135.0.0 - - [30/Apr/2020:14:30:17 -0500] "GET /images/hm_bg.jpg HTTP/1.0" 200 24736"""},"fields": {"http.verb": ["GET"],"http.clientip": ["40.135.0.0"],"http.response": [200],"message": ["""40.135.0.0 - - [30/Apr/2020:14:30:17 -0500] "GET /images/hm_bg.jpg HTTP/1.0" 200 24736"""],"http.client_ip": ["40.135.0.0"],"timestamp": ["2020-04-30T19:30:17.000Z"]}}]}
}

另外,还记得脚本中的 if 语句吗?

if (clientip != null) emit(clientip);

如果脚本不包含此条件,则查询将在任何与模式不匹配的分片上失败。 通过包含此条件,查询会跳过与 grok 模式不匹配的数据。

搜索特定范围内的文档

你还可以运行对时间戳字段进行操作的范围查询。 以下查询返回时间戳大于或等于 2020-04-30T14:31:27-05:00 的任何文档:

GET my-index-000001/_search?filter_path=**.hits
{"query": {"range": {"timestamp": {"gte": "2020-04-30T14:31:27-05:00"}}}
}

响应包括日志格式不匹配但时间戳在定义范围内的文档。

使用 dissect 模式定义运行时字段

如果你不需要正则表达式的强大功能,你可以使用解剖模式而不是 grok 模式。 解剖模式匹配固定的分隔符,但通常比 grok 更快。

你可以使用 dissect 来获得与使用 grok 模式解析 Apache 日志相同的结果。 你不匹配日志模式,而是包括要丢弃的字符串部分。 特别注意要丢弃的字符串部分将有助于构建成功的解析模式。

PUT my-index-000001/_mappings
{"runtime": {"http.client.ip": {"type": "ip","script": """String clientip=dissect('%{clientip} %{ident} %{auth} [%{@timestamp}] "%{verb} %{request} HTTP/%{httpversion}" %{status} %{size}').extract(doc["message"].value)?.clientip;if (clientip != null) emit(clientip);"""}}
}

同样,你可以定义一个解析模式来提取 HTTP 响应代码:

PUT my-index-000001/_mappings
{"runtime": {"http.responses": {"type": "long","script": """String response=dissect('%{clientip} %{ident} %{auth} [%{@timestamp}] "%{verb} %{request} HTTP/%{httpversion}" %{response} %{size}').extract(doc["message"].value)?.response;if (response != null) emit(Integer.parseInt(response));"""}}
}

然后,你可以运行查询以使用 http.responses 运行时字段检索特定的 HTTP 响应。 使用 _search 请求的 fields 参数来指示你要检索的字段:

GET my-index-000001/_search?filter_path=**.hits
{"query": {"match": {"http.responses": "304"}},"fields" : ["http.client_ip","timestamp","http.verb"]
}

响应包括单个文档,其中 HTTP 响应为 304:

{"hits": {"hits": [{"_index": "my-index-000001","_id": "an7rTIUBIjh__4nuBm2T","_score": 1,"_source": {"timestamp": "2020-04-30T14:31:22-05:00","message": """247.37.0.0 - - [30/Apr/2020:14:31:22 -0500] "GET /images/hm_nbg.jpg HTTP/1.0" 304 0"""},"fields": {"http.verb": ["GET"],"http.client_ip": ["247.37.0.0"],"timestamp": ["2020-04-30T19:31:22.000Z"]}}]}
}

Elasticsearch:使用 runtime fields 探索你的数据相关推荐

  1. Elasticsearch:Runtime fields 及其应用(二)

    这是继上一篇文章 "Elasticsearch:Runtime fields 及其应用(一)" 的续篇. 索引运行时字段 运行时字段由它们运行的上下文定义. 例如,你可以在搜索查询 ...

  2. Elasticsearch:Runtime fields 及其应用(一)

    在之前的很多文章中,我详述了如何使用 runtime fields.在今天的文章中,我想更多地介绍 runtime fields 及其一些用例. 我们知道, 从历史上看,Elasticsearch 依 ...

  3. 【Elasticsearch】Elasticsearch:Runtime fields 入门, Elastic 的 schema on read 实现 - 7.11 发布

    1.概述 转载:Elasticsearch:Runtime fields 入门, Elastic 的 schema on read 实现 - 7.11 发布

  4. elasticsearch中best fields策略dis_max和tie_breaker详解

    参考:elasticsearch中best fields策略dis_max和tie_breaker详解 概念 常规multi-field搜索结果分析 dis_max参数设置 一.为帖子数据增加cont ...

  5. 《机器学习与R语言(原书第2版)》一2.3 探索和理解数据

    本节书摘来自华章出版社<机器学习与R语言(原书第2版)>一书中的第2章,第2.3节,美] 布雷特·兰茨(Brett Lantz) 著,李洪成 许金炜 李舰 译更多章节内容可以访问云栖社区& ...

  6. Elasticsearch安装及自动同步mysql数据库数据

    2019独角兽企业重金招聘Python工程师标准>>> Elasticsearch安装及自动同步mysql数据库数据 1           环境: CentOS  6.4  x64 ...

  7. 【elasticsearch】ES数据库重建索引 -- Reindex(数据迁移)

    1.应用背景: 1.当你的数据量过大,而你的索引最初创建的分片数量不足,导致数据入库较慢的情况,此时需要扩大分片的数量,此时可以尝试使用Reindex. 2.当数据的mapping需要修改,但是大量的 ...

  8. 探索 Apple 公司股价数据-Python实现

    探索 Apple 公司股价数据 环境和数据 题目 代码 详解 这个例子比较适合数据挖掘入门一段时间.都还是比较基础的一些函数的应用. 环境和数据 这里使用的是pycharm2020.1.1 x64的 ...

  9. 汽车大数据探索从汽车数据营销发起

    文章讲的是 汽车大数据探索从汽车数据营销发起, 汽车正式诞生于19世纪工业革命后,之后每一次人类的科技进步都使汽车的发展迈进一个新的台阶.直到20世纪末,由于汽车与人们的生产生活联系得越发紧密,人们对 ...

最新文章

  1. 使用java的Calendar对象获得当前日期的上几个度开始、结束时间
  2. jboss学习 - vfs---转载
  3. 深度学习和目标检测系列教程 8-300:目标检测常见的标注工具LabelImg和将xml文件提取图像信息
  4. kitten编程猫里的工具函数
  5. mybatis mysql demo_SpringBoot(入门Demo,整合mybatis,连接mysql)
  6. java jvm对象_Java对象在JVM中长啥样
  7. 《大话软件工程—需求分析与软件设计》,给出了分析与设计过程中需要的理论、方法、工具和标准
  8. 任正非:未来是云时代,华为也要转向云战略
  9. 树莓派安装rtl8192eu无线网卡驱动
  10. KindEditor 基本的使用
  11. 计算机网络的文件怎么删除,教你一招如何删除Win7电脑中的顽固文件
  12. 【数据结构06】二叉平衡树(AVL树)
  13. windows11鼠标滚轮反向
  14. 我们为什么要做接口管理平台 YApi
  15. 解决VMware虚拟机无法上网
  16. 反病毒工具-WinDBG
  17. 世界上到底有多少种编程语言?
  18. keras UserWarning: Discrepancy between trainable weights and collected trainable weigh...
  19. 鹅厂前员工稍微谈谈腾讯
  20. 算法【二叉树】学习笔记 - 已知结点数计算可构建出多少种二叉树

热门文章

  1. python手写均值滤波锐化_中值滤波,均值滤波,锐化滤波原理
  2. Python E化-自制有道英文翻译器
  3. windows10录屏截屏快捷键及使用方法
  4. 关于解码问题的解决方案——终极解码
  5. FCN全卷积网络—upsampling(上采样)——OpenCV图像金字塔
  6. 如何将一个多位数按照位数分割?
  7. vue中将网页以pdf格式下载
  8. Matlab导出高清图片、且Word中压缩不失真、转换PDF不失真
  9. SAP HR技术系列之一:e-HR,不只是看起来很美
  10. python中csv文件的创建、读取、修改等操作总结