ELK实践(二)--多行日志处理
前言:
上文https://www.jianshu.com/p/8b68ac58b191通过logstash grok进行数据处理,提取出日志类型及message,并可以生成到不同的索引文件;
随后的使用中发现针对错误日志,由于日志信息换行导致message会分散到多条日志中,如下图所示:
日志内容(部分)如下
[2020-04-06 15:55:14.521] |http-nio-8020-exec-10|ERROR| com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Exception during pool initialization.
com.mysql.cj.jdbc.exceptions.CommunicationsException: Communications link failure
The last packet sent successfully to the server was 0 milliseconds ago. The driver has not received any packets from the server.
at com.mysql.cj.jdbc.exceptions.SQLError.createCommunicationsException(SQLError.java:174)
at com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:64)
at com.mysql.cj.jdbc.ConnectionImpl.createNewIO(ConnectionImpl.java:836)
at com.mysql.cj.jdbc.ConnectionImpl.<init>(ConnectionImpl.java:456)
kibana错误日志显示
1.多行处理
目前针对多行合并处理通常采用的logstash的multiline插件完成。
笔者采用如下方式处理时,发现无法启动logstash,提示multiline不被beats支持,进入docker查看 codec的multiline插件是安装过的。
input {
beats {
port => 5044
codec => multiline {
pattern => "^\[\d*-\d*"
negate => true
what => "previous"
}
}
}
查阅官方资料的确有显示该功能不被支持,如果要合并则需要我们从源头配置Filebeat去处理,只能姑且理解为ES期望这种多行文本合并的操作最好在源头解决掉。
https://www.elastic.co/guide/en/beats/filebeat/6.8/multiline-examples.html
解决方案
采集日志我们采用了log-pilot,其内部使用了filebeat,我们修改其filebeat模式后重新部署log-pilot。
**master节点**
[root@k8s-master es]# kubectl get pods -n kube-system | grep log-pilot
log-pilot-9pt27 1/1 Running 0 3d17h
log-pilot-qq9w5 1/1 Running 1 3d17h
log-pilot-vc7qs 1/1 Running 0 3d17h
kubectl cp kube-system/log-pilot-9pt27:/pilot/filebeat.tpl filebeat.tpl #复制容器中filebeat.tpl文件到master节点
如上文提及,我们的日志格式如下:
***********.Net日志**************
[2020-04-05 12:59:04.7338]|netCore07.LogHelper|INFO|请求Healthy:04/05/2020 12:59:04
[2020-04-05 12:59:16.3861]|netCore07.LogHelper|INFO|请求DefaultIndex:04/05/2020 12:59:16
[2020-04-05 12:59:17.5992]|netCore07.LogHelper|INFO|请求DefaultIndex:04/05/2020 12:59:17
[2020-04-05 12:59:18.1920]|netCore07.LogHelper|INFO|请求DefaultIndex:04/05/2020 12:59:18
[2020-04-05 12:59:21.6663]|netCore07.LogHelper|INFO|请求Healthy:04/05/2020 12:59:21
***********Java日志**************
[2020-04-05 13:16:58.351] |http-nio-8020-exec-1|INFO| o.a.c.core.ContainerBase.[Tomcat].[localhost].[/] - Initializing Spring DispatcherServlet 'dispatcherServlet'
[2020-04-05 13:16:58.351] |http-nio-8020-exec-1|INFO| org.springframework.web.servlet.DispatcherServlet - Initializing Servlet 'dispatcherServlet'
[2020-04-05 13:16:58.375] |http-nio-8020-exec-1|INFO| org.springframework.web.servlet.DispatcherServlet - Completed initialization in 22 ms
[2020-04-05 13:16:58.434] |http-nio-8020-exec-1|INFO| com.xxx.controller.StudentController - File-springboot-say:xx
针对日志我们编写一个正则匹配每条日志的开头格式
^.{0,3}\[[0-9]{4}-[0-9]{2}-[0-9]{2}.{0,20}\]|^[0-9]{4}-[0-9]{2}-[0-9]{2}.{0,20}
vim filebeat.tpl #修改该文件
{{range .configList}}
{{range .configList}}
{{range .configList}}
{{range .configList}}
scan_frequency: 10s
fields_under_root: true
{{if .Stdout}}
docker-json: true
{{end}}
{{if eq .Format "json"}}
json.keys_under_root: true
{{end}}
fields:
{{range $key, $value := .Tags}}
{{ $key }}: {{ $value }}
{{end}}
{{range $key, $value := $.container}}
{{ $key }}: {{ $value }}
{{end}}
tail_files: false
close_inactive: 2h
close_eof: false
close_removed: true
clean_removed: true
close_renamed: false
#以下4行是我们手动添加的
multiline.pattern: '^.{0,3}\[[0-9]{4}-[0-9]{2}-[0-9]{2}.{0,20}\]|^[0-9]{4}-[0-9]{2}-[0-9]{2}.{0,20}'
multiline.negate: true
multiline.match: after
multiline.max_lines: 1000
multiline.timeout: 20s
{{end}}
参数说明:
multiline.negate: 多行匹配模式是否取反,默认false
multiline.match: 定义多行内容被添加到模式匹配行之后还是之前,默认无,可以被设置为after或者before
multiline.max_lines: 单一聚合最大的行数,超过会被丢弃
multiline.timeout:多行匹配超时时间,超过超时时间后的当前多行匹配事件将停止并发送,然后开始一个新的多行匹配事件,默认5秒,这个很有用,以往我们用logstash的multiline时就会遇到最后一条日志无法采集,需要新日志插入才能采集的情况。
文件编写完毕后我们重新制作一个定制版log-pilot镜像:
vim Dockerfile
FROM registry.cn-hangzhou.aliyuncs.com/acs/log-pilot:0.9.6-filebeat
COPY filebeat.tpl /pilot/filebeat.tpl
####wq保存dockerFile
####然后执行docker build制作镜像
[root@k8s-master log-pilot]# docker build -t 192.168.0.230:8083/es/log-pilot .
Sending build context to Docker daemon 3.584kB
Step 1/2 : FROM registry.cn-hangzhou.aliyuncs.com/acs/log-pilot:0.9.7-filebeat
---> 10a688e1229a
Step 2/2 : COPY filebeat.tpl /pilot/filebeat.tpl
---> 433f59cc094e
Successfully built 433f59cc094e
Successfully tagged 192.168.0.230:8083/es/log-pilot:latest
####上传镜像到仓库
[root@k8s-master log-pilot]# docker push 192.168.0.230:8083/es/log-pilot
The push refers to repository [192.168.0.230:8083/es/log-pilot]
5d508534be62: Pushed
385d5e9ef470: Pushed
60121972171a: Pushed
f48d07bb0c66: Pushed
93d544c0cc28: Pushed
b204552c6406: Pushed
721384ec99e5: Pushed
latest: digest: sha256:e8cba23fec45b221df0fc6e7b640c0475085dce4e39e14c0eb77cfd6571115c4 size: 1788
重新部署log-pilot
kubectl delete -f log-pilot.yaml
删除当前的log-pilot
接下来修改log-pilot.yaml中镜像地址为我们刚才制作的镜像完整名称
kubectl apply -f log-pilot.yaml
再重新部署log-pilot
2.验证
重新访问下报错的URL路径,系统记录下报错日志,然后我们到kibana中查看
从容器日志文件中查看,出现2条错误日志,内容很长,文中就不再粘贴。
kibana中查看,和预期一样,记录了两条完整的日志
kibana多行日志整合
说明:列表中是摘要,点击列表行可以查看单个文档,查看完整日志信息。
总结
这种从源头解决多行的问题并不是笔者所期望的,配置方式的灵活度并不如logstash的脚本模式,您也可能尝试通过logstash的filter中使用multiline模式达到同样的效果,示例:
filter {
multiline {
pattern => "^.{0,3}\[[0-9]{4}-[0-9]{2}-[0-9]{2}.{0,20}\]|^[0-9]{4}-[0-9]{2}-[0-9]{2}.{0,20}"
negate => true
what => "previous"
}
grok {
match => {"message" => ".{0,3}%{TIMESTAMP_ISO8601:logTime}.{0,10}\|.*\|%{LOGLEVEL:logLevel}\|%{GREEDYDATA:message}"}
#用上面提取的message覆盖原message字段
overwrite => ["message"]
}
}