Fluentd采集日志超出docker容器16k限制换行问题

问题描述

java服务产生的大日志收集到ES被拆分成多行,ES中被拆分的日志如下:

图片1

如上所示,红框中的日志本应该是一条,存到ES里结果拆分为了4条日志。

问题排查

于是,就去服务器上找这几条原始日志,如下:

图片2

通过对比发现和ES日志完全一样,这说明日志并不是在Fluentd进行收集的时候拆分的。

分析问题

根据观察被拆分处也没有规律,唯一的规律就是日志都特别长,显然日志很有可能是在写入docker stdout时就被拆分了。

确定问题

通过搜索发现,在docker github项目上也有人遇到过该问题,

图片3

大概的意思是说:在docker 1.13以后的版本中引入了日志更改,如果消息是长度超过16KB的JSON对象,docker会将其拆分。

此问题,在docker 源码中也得到了证实。

解决问题

既然导致日志拆分的原因已经找到了,大致可以从以下3个方面解决大日志拆分

  • 把docker版本降到1.13以下
  • 修改docker源码,然后重新编译
  • 在Fluentd中对拆分的日志进行合并

经过分析发现前面两个在实施的过程中会对现有服务产生较大影响(因为现有服务大多数只运行了一个pod),既然这样那只有实施第3个方案了。

顺着docker github上的问题,找到了一个可以合并多行日志的插件,随后经过多番测试成功合并了因docker 16k限制而拆分的大日志。

合并后的结果如下:

图片4

前面的7条日志,最终合并成了4条

Fluentd 配置

经过调试后,最终fluentd的配置如下,主要就是增加了concat多行合并配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# cat fluentd.conf

#日志源配置,格式化成json
<source>
@id fluentd-containers.log
@type tail
path /var/log/containers/*/*.log
pos_file /var/log/es-containers.log.pos
tag raw.kubernetes.*
<parse>
@type multi_format
<pattern>
format json
time_key timestamp
time_format %Y-%m-%dT%H:%M:%S.%NZ
</pattern>
<pattern>
format /^(?<time>.+) (?<stream>stdout|stderr) [^ ]* (?<log>.*)$/
time_format %Y-%m-%dT%H:%M:%S.%N%:z
</pattern>
</parse>
</source>

# 检测java异常栈日志,并作为一条日志转发
# Detect exceptions in the log output and forward them as one log entry.
<match raw.kubernetes.**>
@id raw.kubernetes
@type detect_exceptions
remove_tag_prefix raw
message log
stream stream
languages java
multiline_flush_interval 5
max_bytes 0
max_lines 1000
</match>

#排除以回车符开头和结尾的日志
<filter kubernetes.**>
@type grep
<exclude>
key log
pattern /^\n$/
</exclude>
</filter>

#将json日志log中的value解析成message字段内容,不使用则es中message会变成log字段
<filter kubernetes.**>
@id filter_parser
@type parser # multi-format-parser多格式解析器插件
key_name log # 在要解析的记录中指定字段名称。
reserve_data true # 在解析结果中保留原始键值对。
remove_key_name_field true # key_name 解析成功后删除字段。
<parse>
@type multi_format
<pattern>
format json
</pattern>
<pattern>
format none
</pattern>
</parse>
</filter>

#多行日志拼接,主要针对pod日志超过docker 16k限制的大日志
<filter kubernetes.**>
@type concat
key message
separator ""
multiline_start_regexp /^\[\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}.\d{3}\]/
multiline_end_regexp /\n$/ #需要匹配结束特征,否则遇到带有大日志的文件时会质押最后一条日志
flush_interval 10
</filter>

#添加k8s相关的元数据,pod namespace id 等
<filter kubernetes.**>
@id filter_kubernetes_metadata
@type kubernetes_metadata
</filter>
-------------本文结束感谢您的阅读-------------