需求
初步想法是用Nginx将所有服务的请求输出到日志,再由收集器将日志 收集起来->解析->存储。收集所有服务的请求日志,存储以供后续使用。
使用ES家族的 filebeat 收集NG日志,可是收集到的日志不太好解析,而且还增加个时间字段,转换某些字段等操作,后来想法是再写个服务(或Logstash)吧 Filebeat调这个服务,由这个服务做处理解析。但是又感觉太重了,偶然发现 ES 原生 已经支持数据处理(filter)了。
于是花了半天研究了下,下面把坑、和使用方法都记一下。
具体参考了优秀的这篇文章:
关于 Ingest Node/Pipeline 的详解。赞!
https://www.felayman.com/articles/2017/11/24/1511527532643.html
还有官方文档:
https://www.elastic.co/guide/en/elasticsearch/reference/current/handling-failure-in-pipelines.html
先看下效果吧:
【源】NG log
61.50.98.62 - [08/Jan/2019:13:11:09 +0800] "GET /images/index/img05_11.jpg HTTP/1.1" 200 312473 "http://vipcode.cn/" - 0.210 0.052 100.118.58.9:80 200 Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3494.0 Safari/537.36
要做的事情:
- 增加 gmt_create 记录数据时间。
- 将以空格分隔的NG日志 解析为 图中的K->V
解决过程:
- create index ... 略过。
- 创建pipeline(processor)增加gmt_create
- 创建pipeline(processor)解析 ng log
2、3步最终是要合一起的,一个processor即增加了时间也解析了格式;可我初次试验的时候是分开调试的。
其中用到了 set 和 grok (在上面的连接中有介绍)
_ingest/pipeline/nglogproc
这里坑有两个,
- {{_ingest.timestamp}} 存到ES里的时间是UTC0的时间,晚于国内8小时。
- grok 的调试比较烦人,虽说写比较容易,比对数据是最耗时的。
最后,post ES 处理保存数据:解决方案:
- 因为 _ingest.timestamp 是ES生成的,没有找到通过哪个配置能修改,想了一晚上最后想编译源码解决去了。还好,不用管,Kibana会正常显示、检索。
- Grok语法和内置的表达式网上有很多,熟悉一下然后 根据这个调试器
http://grokdebug.herokuapp.com
,一点点来吧。
没有出错就是文章开始的效果图了,_ingest.timestamp 不会出错,会出问题的多半出在grok和源数据不匹配的问题里,错误里会有部分提示。
One more thing, 贴出我的 grok
Log1:
112.17.88.223 - [08/Jan/2019:10:49:08 +0800] "GET /open_008/08_img6.png HTTP/1.1" 200 1445 "https://www.vipcd.com/_zh/open_008.min.css" - 0.005 0.004 59.110.185.113:80 200 Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36
Log2:
39.104.152.143 - [08/Jan/2019:17:34:18 +0800] "GET / HTTP/1.1" 200 9262 "-" - 0.032 0.031 172.17.231.247:8080 200 Go-http-client/1.1
Log3:
39.82.11.241 - [22/Jan/2019:16:53:04 +0800] "GET /v1?uuid=vipcode2165376223244&uid=154814718762578s5dfaco&project=act&logType=pv&logVersion=1.0&refer=&ua=Mozilla%2F5.0%20(iPad%3B%20U%3B%20CPU%20OS%205_0%20like%20Mac%20OS%20X%3B%20en-us)%20AppleWebKit%2F534.46%20(KHTML%2C%20like%20Gecko)%20Version%2F5.1%20Mobile%2F9A334%20Safari%2F7534.48.3&subType=guangdiantong%7C%7ChasCode&screensize=w800||h1280&url=http%3A%2F%2Fact.vipcode.com%2Fmps%2Fproduce%2F1545381467902%2Fpc%2Findex.html%3FstClId%3D7%26sLClId%3D0%26plId%3D0%26unId%3D0%26kwd%3D%26lGPId%3D150%26acId%3D0%26pSId%3D28%26acPFlag%3D0%26sFFlag%3D0%26remark%3D113Lllq122105%26qz_gdt%3Df7mumxf7aaaoaehyrkya HTTP/1.1" 200 1 "http://act.vipcode.com/mps/produce/1545381467902/pc/index.html?stClId=7&sLClId=0&plId=0&unId=0&kwd=&lGPId=150&acId=0&pSId=28&acPFlag=0&sFFlag=0&remark=113Lllq122105&qz_gdt=f7mumxf7aaaoaehyrkya" - 0.000 - - - Mozilla/5.0 (iPad; U; CPU OS 5_0 like Mac OS X; en-us) AppleWebKit/534.46 (KHTML, like Gecko) Version/5.1 Mobile/9A334 Safari/7534.48.3
Grok:
%{IP:client} %{USER:user} \[%{HTTPDATE:http_date}\] \"%{WORD:method} %{NOTSPACE:http_uri} HTTP/%{NUMBER:http_ver}\" %{NUMBER:http_st_ng} %{NUMBER:bytes} \"(?<referer>https?://[^ ]+|-)\" (?<x_forwarded_for>[0-9]+(\.[0-9]+){3}(?:, [0-9]+(\.[0-9]+){3})*|-) %{NUMBER:rsp_time1} (?:%{NUMBER:rsp_time2}|-) (?<target_server>[0-9]+(\.[0-9]+){3}(:[0-9]+)?|-) (?:%{NUMBER:http_st_server}|-) (?<u_agent>.*$)
nglogproc Pipleline:
{
"description": "nginx log processor",
"processors": [
{
"grok": {
"field": "message",
"patterns": [
"%{IP:client} %{USER:user} \\[%{HTTPDATE:http_date}\\] \"%{WORD:method} %{NOTSPACE:http_uri} HTTP/%{NUMBER:http_ver}\" %{NUMBER:http_st_ng} %{NUMBER:bytes} \"(?<referer>[^\"]+|-)\" (?<x_forwarded_for>[0-9]+(\\.[0-9]+){3}(?:, [0-9]+(\\.[0-9]+){3})*|-) %{NUMBER:rsp_tn} (?<rsp_ts>[0-9.]+(, [0-9.]+)*|-) (?<tar_server>[0-9]+(\\.[0-9]+){3}(:[0-9]+)?(, ([0-9]+(\\.[0-9]+){3}(:[0-9]+)?))*|-) (?<http_st_server>[0-9]+(, [0-9]+)*|-) (?<u_agent>.*$)"
],
"on_failure": [
{
"set": {
"field": "g_err0",
"value": "{{_ingest.on_failure_message }}"
}
}
]
}
},
{
"set": {
"field": "gmt_create",
"value": "{{_ingest.timestamp}}"
}
},
{
"grok": {
"field": "http_uri",
"patterns": [
"/v\\d+\\?uuid=(?<uuid>[^&]*+)&uid=(?<uid>[^&]*+)&project=(?<project>[^&]*+)&logType=(?<logType>[^&]*+)&logVersion=(?<logVersion>[^&]*+)&refer=(?<refer>[^&]*+)&ua=(?<ua>[^&]*+)&subType=(?<subType>[^&]*+)&screensize=(?<screensize>[^&]*+)&url=(?<url>.*+$)"
],
"on_failure": [
{
"set": {
"field": "g_err1",
"value": "{{_ingest.on_failure_message }}"
}
}
]
}
},
{
"urldecode": {
"field": "ua",
"on_failure": [
{
"set": {
"field": "g_err2",
"value": "{{_ingest.on_failure_message }}"
}
}
]
}
},
{
"urldecode": {
"field": "url",
"on_failure": [
{
"set": {
"field": "g_err2",
"value": "{{_ingest.on_failure_message }}"
}
}
]
}
},
{
"urldecode": {
"field": "screensize",
"on_failure": [
{
"set": {
"field": "g_err2",
"value": "{{_ingest.on_failure_message }}"
}
}
]
}
},
{
"urldecode": {
"field": "subType",
"on_failure": [
{
"set": {
"field": "g_err2",
"value": "{{_ingest.on_failure_message }}"
}
}
]
}
}
]
}
这种方式的好处是非常简便,缺点是容错率非常低对格式有着严格的要求。
不过庆幸的是其提供了“当错误发生时你要做的事情 —— on_failure ”
Filebeat 使用 Pipeline
http://www.axiaoxin.com/article/236/
在FB配置文件中增加配置即可。