一、Fluentd日志解析的核心矛盾:JSON vs 正则
咱们做开发的,肯定都碰过日志的坑——程序跑起来一堆乱码一样的文本,要从里面扒出有用的信息,全靠日志解析。Fluentd是现在很多人用来收日志、转格式、存到数据库的工具,它最核心的一步就是“解析日志内容”,而解析的时候,很多人会卡在两个选项里:用普通JSON解析,还是用正则表达式?
这俩看起来都是“从一堆数据里拆出有用字段”的方法,但实际用起来差别大得很。尤其是不同业务的负载不一样,比如有的业务每秒只产生10条日志,有的每秒能产生10万条,这俩解析方法的差距就会被放得特别大,最后连带着过滤、存数据库的整个流程都出问题。
1.1 先搞懂两个解析方式到底是什么
先给大家举个最接地气的例子,咱们拿一条真实的业务日志来看。比如一个电商的下单服务,日志长这样:
2024-05-20 14:30:25 INFO 下单成功,订单ID=12345,用户ID=67890,金额=99.99
这条日志里,有用的信息是“订单ID、用户ID、金额”,解析就是要把这三个信息从整行文本里单独拆出来,变成像“{订单ID:12345, 用户ID:67890, 金额:99.99}”这样的结构化数据,方便后面处理。
1.1.1 普通JSON解析:适合“标准格式化”的日志
首先说普通JSON解析,它的要求很简单:日志本身得是标准的JSON格式。什么是标准JSON?就是得有固定的大括号、双引号、冒号,不能有乱七八糟的字符。比如把上面那条日志改成标准JSON格式,应该是这样:
{
"time": "2024-05-20 14:30:25",
"level": "INFO",
"message": "下单成功",
"order_id": "12345",
"user_id": "67890",
"amount": "99.99"
}
JSON解析的逻辑特别简单:Fluentd拿到这条日志,直接按照JSON的语法规则,把每个键(比如order_id)对应的值(比如12345)提取出来就行,不用自己写规则。
1.1.2 正则解析:适合“非标准”的日志
那如果日志不是标准JSON呢?比如开头那条没有大括号、双引号的日志,这时候就得用正则解析。正则是什么?你可以把它理解成“文本里找特定内容的公式”。比如针对开头那条日志,我们写一个正则公式:
(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) (\w+) (.*),订单ID=(\d+),用户ID=(\d+),金额=(\d+\.\d+)
这个公式的意思是:从整行文本里,按顺序找出“时间(数字-数字-数字 数字:数字:数字)”、“日志级别(字母)”、“消息内容(任意字符)”、“订单ID(数字)”、“用户ID(数字)”、“金额(数字.数字)”这几个部分。Fluentd拿到日志,就按照这个公式,把每个括号里的内容对应到我们指定的字段名上,完成解析。
1.2 两种解析方式的优缺点对比
光知道是什么还不够,得知道什么时候用哪个,这就得分清它们的优缺点。
1.2.1 普通JSON解析的优缺点
优点很明显: 第一,简单。不用自己写复杂的公式,只要日志是标准JSON,Fluentd就能直接解析,不容易出错。比如刚才的JSON日志,Fluentd的配置只要写一行“解析格式是JSON”就行,不用管日志里有多少个字段,加新字段也不用改配置。 第二,快。JSON的语法是固定的,Fluentd解析的时候不用做复杂的匹配计算,速度特别快。比如每秒处理10万条JSON日志,对Fluentd来说毫无压力。 第三,稳定。只要日志是标准JSON,解析成功率几乎是100%,不会出现“公式写错了,某条日志解析不出来”的情况。
缺点也很致命:太依赖日志的格式。如果日志不是标准JSON,比如有的日志里有单引号、有的有换行、有的字段名大小写不统一,JSON解析就会直接失败。比如下面这条日志,因为把双引号写成了单引号,JSON解析就认不出来:
{
'time': '2024-05-20 14:30:25',
'level': 'INFO',
'message': '下单成功',
'order_id': '12345',
'user_id': '67890',
'amount': '99.99'
}
另外,很多老系统的日志是早就写好的,不可能为了Fluentd的解析要求,把所有日志都改成标准JSON,这时候JSON解析就用不了。
1.2.2 正则解析的优缺点
优点刚好能补JSON的缺点: 第一,灵活。不管日志是什么格式,只要你能写出对应的正则公式,就能解析。比如老系统的非标准日志、有特殊格式的日志、甚至是乱码里的关键信息,都能靠正则挖出来。 第二,不依赖日志的生成方。不用改业务代码,不用调整日志输出,只要Fluentd这边写好正则,就能解析,适合处理存量的、改不了的日志。
缺点也特别突出: 第一,复杂。正则公式特别难写,也特别容易错。比如刚才的正则公式,要是日志里的“订单ID”写成“订单id”(大小写变了)、或者金额的小数点前面没有数字(比如“.99”)、或者某条日志里没有“用户ID”这个字段,整个公式就会匹配失败,这条日志就解析不出来。 第二,慢。正则解析的逻辑是“按公式逐个字符匹配”,计算量特别大。比如每秒处理10万条日志,用正则的话,Fluentd的CPU占用率可能直接拉满,甚至处理不完,导致日志堆积。 第三,维护难。如果业务日志的格式变了,比如原来的“订单ID”改成“order_id”,就得改正则公式,改完还得测试所有可能的日志情况,很麻烦。
二、业务负载差异放大解析吞吐差距
刚才说的是两种解析方式的基础差距,那为什么业务负载不一样,差距会被放大呢?咱们得先搞清楚“解析吞吐”是什么——就是Fluentd每秒能解析多少条日志。
举个实际的场景例子,咱们用的技术栈是Fluentd v1.16(当前稳定版),搭配JSON解析插件(内置)和正则解析插件(内置的parse插件),来测两种解析方式的吞吐差距。
2.1 低负载场景:差距不明显
先看低负载场景:比如一个内部管理系统,每秒只产生10条日志,每条日志的长度大概是200个字符。
- 用JSON解析的话,Fluentd每秒能处理10万条以上,10条的负载对它来说就是“挠痒痒”,完全没压力。
- 用正则解析的话,Fluentd每秒能处理1万条以上,10条的负载也能轻松应对。
这时候两种方式的差距只有10倍,但因为负载太低,差距完全体现不出来,不管用哪个都没问题。
2.2 高负载场景:差距被放大
再看高负载场景:比如一个电商的支付服务,每秒产生10万条日志,每条日志的长度也是200个字符。
- 用JSON解析的话,Fluentd的CPU占用率大概在30%左右,每秒能稳定处理10万条,完全跟得上负载。
- 用正则解析的话,Fluentd的CPU占用率直接冲到90%以上,每秒只能处理1万条左右,剩下的9万条日志会堆积在Fluentd的内存队列里。
这时候差距就从10倍变成了“能不能处理完”的生死差距。而且随着负载越来越高,差距会越来越大:比如负载到每秒20万条,JSON解析的Fluentd可能CPU只用到50%,而正则解析的Fluentd可能直接宕机,因为内存队列满了,新的日志直接被丢弃。
为什么差距会被放大?核心原因是正则解析的“计算复杂度”是跟日志长度、日志数量成正比的。每条日志都要做一次复杂的正则匹配,10万条日志就是10万次复杂计算,而JSON解析是固定复杂度的,不管多少条,都是按语法规则快速提取。
2.3 解析吞吐差距的连锁反应:影响过滤与落库
解析只是Fluentd流程的第一步,后面还有“过滤”和“落库”,解析的差距会直接影响后面两步。
2.3.1 对过滤的影响
过滤是什么?就是Fluentd把解析出来的结构化数据,按照规则筛选出有用的日志。比如只保留“金额大于1000元”的下单日志,只保留“错误级别”的日志。
如果解析慢,日志堆积,会导致过滤也跟着慢。比如正则解析的Fluentd,每秒只能处理1万条解析好的日志,那过滤模块每秒也只能处理1万条,剩下的解析好的日志又会堆积,整个流程都卡。
2.3.2 对落库的影响
落库是什么?就是把过滤好的日志存到数据库里,比如Elasticsearch、MySQL、ClickHouse这些。
解析慢导致的日志堆积,会让落库的压力变大:原来每秒落10万条,现在突然有10万条堆积的日志要落库,数据库的写入压力会瞬间飙升,甚至导致数据库写入超时、锁表。
反过来,落库慢又会反过来影响解析:落库慢导致过滤好的日志堆积,过滤模块堆积导致解析好的日志堆积,解析模块的内存队列满了,新的日志直接被丢弃,整个链路就断了。
三、不同场景下的选择策略
知道了两种解析方式的差距和影响,那我们该怎么选?核心是“匹配业务场景”。
3.1 适合用普通JSON解析的场景
- 业务日志是标准JSON格式的。比如现在很多新的服务,用Logback、Log4j2这些日志框架,直接配置输出标准JSON日志,这种情况优先用JSON解析。
- 业务负载高的。比如每秒产生1万条以上日志的服务,用JSON解析能保证解析速度,避免链路卡壳。
- 日志格式经常变化的。比如业务迭代快,经常加新的字段,JSON解析不用改配置,能自动适配新字段。
3.2 适合用正则解析的场景
- 业务日志不是标准JSON格式的。比如老系统的日志、非结构化的日志、特殊格式的日志,只能用正则解析。
- 业务负载低的。比如每秒产生1000条以下日志的服务,正则解析的速度能满足需求,不会影响链路。
- 日志格式固定的。比如老系统的日志,格式好几年都不会变,正则公式写一次就能用很久,维护成本低。
3.3 折中方案:先做JSON兼容,再解析
如果业务日志不是标准JSON,但负载又高,有没有折中方案?有!就是先让业务方把日志改成“准JSON”格式,再用JSON解析。
比如原来的日志是:
2024-05-20 14:30:25 INFO 下单成功,订单ID=12345,用户ID=67890,金额=99.99
让业务方稍微改一下日志输出,改成下面这种“准JSON”格式(其实是标准JSON,只是看起来和原来的日志类似):
{
"time": "2024-05-20 14:30:25",
"level": "INFO",
"message": "下单成功",
"order_id": "12345",
"user_id": "67890",
"amount": "99.99"
}
这样既不用完全推翻原来的日志内容,又能享受JSON解析的速度和稳定性,是很多高负载非标准日志场景的最优解。
四、注意事项
不管选哪种解析方式,都有几个要注意的地方。
4.1 正则解析的注意事项
- 正则公式要尽量简单。不要写太复杂的正则,比如不要嵌套太多括号、不要用太复杂的匹配规则,能简化就简化,这样能提高解析速度。
- 要做容错处理。比如有的日志可能少字段,正则公式要能匹配这种情况,比如用“可选”规则(在正则里用?表示可选),避免整条日志解析失败。
- 要测试所有可能的日志情况。比如测试正常日志、异常日志、少字段的日志、多字段的日志,确保正则公式能覆盖所有情况。
4.2 JSON解析的注意事项
- 要保证日志是标准JSON。比如所有的键和值都要用双引号、不能有换行、不能有特殊字符(比如中文的顿号、逗号),如果有特殊字符,要转义。
- 要避免日志过大。比如单条日志超过1MB,JSON解析的速度会变慢,甚至导致Fluentd内存溢出,要控制单条日志的大小。
4.3 全链路的注意事项
- 要做压力测试。不管选哪种解析方式,都要模拟业务的真实负载,测一下Fluentd的解析速度、过滤速度、落库速度,确保整个链路能承受业务的压力。
- 要监控Fluentd的状态。比如监控Fluentd的CPU占用率、内存占用率、队列长度、解析成功率、落库成功率,一旦出现异常,能及时发现。
五、文章总结
Fluentd的日志解析,普通JSON和正则的取舍,本质上是“速度、稳定性”和“灵活性、兼容性”的取舍。普通JSON解析快、稳,但要求日志是标准格式;正则解析灵活、兼容,但慢、容易出错。而业务负载的差异,会把这种差距放大,进而影响过滤、落库的全链路。
所以在选择的时候,不能盲目选其中一种,要结合业务的日志格式、负载情况、维护成本来选:新的高负载服务,优先用JSON解析;老的低负载服务,用正则解析;高负载但日志不标准的,尽量改成准JSON格式再解析。
最后要记住,日志解析是整个链路的第一步,第一步走稳了,后面的过滤、落库才能顺利进行,整个日志系统才能稳定可靠。
评论
围绕“事实上Fluentd日志解析面临普通JSON与正则路径的取舍,业务负载各异使解析吞吐差异放大,进而影响过滤与落库全链路”参与讨论