Hi~朋友,关注置顶防止错过消息
背景
公司日志格式均为文本格式,且Pattern不固定,对于日志的处理只能使用正则匹配,正则匹配难以完美的处理每种格式的日志,无法自动适配属性,为了解决这种日志混乱且不标准的困境,我们对整个业务日志进行统一调整。
现有日志背景
- 应用部署在k8s中,业务日志使用logback进行打印
- 日志收集采用DaemonSet的Filebeat形式采集各个应用的日志组件
- Filebeat采集日志以后投递到ES
- ES在接收到日志以后,会通过我们自定义Ingest Pipeline处理一遍数据,这里处理数据主要将日志中的事件提取出来写到我们自定义的时间属性中,该属性用于精准排序
- 处理完以后我会写到不同的index中
日志改造
日志统一解决方案主要需要通过以下步骤改造:
- 统一业务日志格式,固定部分字段
- 修改Filebeat配置
- 修改ES的Ingest Pipeline,用来解决时间漂移问题
- 验证日志是否正常
统一业务日志格式
首先我们这里统一了日志格式,日志打印统一使用json,logback配置如下:
代码语言:javascript复制
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<propertyname="JSON_LOG_PATTERN"
value='{
"timestamp": "%d{yyyy-MM-dd HH:mm:ss.SSS}",
"traceId": "%X{trace-id}",
"thread": "%t",
"level": "%level",
"class": "%logger",
"content": "%msg",
"exception": "%exception"
}'/>
<springProfilename="local,test,prod">
<appendername="console"class="ch.qos.logback.core.ConsoleAppender">
<encoderclass="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
<providers>
<pattern>
<pattern>
${JSON_LOG_PATTERN}
</pattern>
</pattern>
</providers>
</encoder>
</appender>
<rootlevel="INFO">
<appender-refref="console"/>
</root>
</springProfile>
</configuration>
整个配置文件很简单,我们定义了json的固定模板,主要有以下固定属性:
- timestamp:日志打印时间
- traceId:traceId用于多应用日志间关联查询
- thread:当前线程
- level:日志级别
- class:打印日志的类
- content:日志内容
- exception:异常堆栈
日志输出到控制台,日志的编码类使用LoggingEventCompositeJsonEncoder,该类需要的包在项目中引入一下:
代码语言:javascript复制<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>6.6</version>
</dependency>
该包在使用时对其他包也有一定的要求,我们使用的6.6的包要求如下,其他版本的大家可以去Github上查看具体说明。
修改Filebeat配置
这一步我们主要修改的是Filebeat的input配置,通过修改filebeat-config的ConfigMap视线,如下:
代码语言:javascript复制apiVersion: v1
kind: ConfigMap
metadata:
name: filebeat-config
namespace: kube-system
labels:
k8s-app: filebeat
data:
filebeat.yml: |-
filebeat.inputs:
- type: container
paths:
- "/var/log/containers/xxx*.log"
json.keys_under_root: true
json.add_error_key: true
json.overwrite_keys: true
json.expand_keys: true
fields:
project: xxx
pipeline: k8s-application-log
这里有几关键设置:
- keysunderroot:这里我们设置为true,如果为false,document会被放在一个key为json的属性,我们这里需要将document会被放在各自的json key上,因此需要设置为true,默认是
- overwrite_keys:是否覆盖原有的key,设置为true以后可以覆盖filebeat默认的key值
- expand_keys:该值设置为true以后,key中如果有.存在,key将会被分割为多个键,{"a.b.c": 123}会被扩展成{"a":{"b":{"c":123}}},这个属性可以根据实际情况进行设置
- adderrorkey:如果设置为true,json反编译失败以后Filebeat会添加error.message和error.type:json两个key
- fields.project:这里我设置应用名称,我们的索引也是通过应用名称为前缀,索引的格式为应用名称-yyyy.MM.dd
- pipeline:指定ES的Ingest Pipeline,该Ingest Pipeline用来解决时间漂移问题
定义Ingest Pipeline
之所以定义Ingest Pipeline,主要是用来解决时间漂移问题,假设我们日志的时间是北京时间,在没有修改Filebeat时区配置时,我们投递到ES的时间会比北京时间多8个小时,因为Filebeat在投递是默认的是UTC时区,因此我这里通过一个自定义的Ingest Pipeline将日志中的时间调整成正确的时间,该Pipeline的流程如下:
从上图可以看出,在该Pipeline中我主要定义了三个Processor:
- 第一个Processor我主要用来将我们的timestamp字段转换成一个带时区的时间,这里我们时区暂时不做修改,让他默认UTC:
2.第二个Processor我们主要通过Java代码来将时间调整成正确的UTC时间:
可以看到我们在这一步将这个带时区的时间减去了8个小时,也就是把北京时间减去了8个小时,这里就是一个真正的UTC时间
3. 第三个Processor我们是将带时区的时间转换成不带时区的时间,如下:
通过上述3步,我们就可以解决时间漂移的问题。
观察日志
上述步骤修改完毕以后,如果配置正确,我们可以看到日志被正确的投递到ES中,我们通过kibana观察的效果图如下:
从上图中可以看到我们有几个属性:
- @timestamp:fiebeat投递日志的时间
- timestamp:业务应用打印日志的时间
- 其他字段:包含timestamp,均属于我们在logback的JSONLOGPATTERN定义好的字段。
后言
我给出的方案或许不是最优,在后续的使用中我们也会根据体验不断优化,大家如果有更好的方案或者大家在配置过程中有问题可以私信我进行讨论,在技术的路上没有一个最完美的方案,合适的正确的即是最好的。