日志采集与结构化:日志级别设计、JSON结构化日志、日志采集工具接入
日志这东西,说简单也简单,说复杂真能坑死人。我在报价路由系统上线初期,就吃过日志混乱的亏——线上出问题,翻半天日志找不到关键信息,全是些没用的调试信息。后来我痛定思痛,把日志体系彻底重构了一遍。今天就跟大家聊聊,一个靠谱的日志系统该怎么搭。
日志级别设计:别让日志变成噪音
日志级别,说白了就是给日志贴个标签,告诉运维人员「这条日志有多重要」。我见过不少团队,日志级别设计得跟没有一样——要么全是INFO,要么DEBUG满天飞。
我个人习惯,报价路由系统至少分五个级别:
| 级别 | 含义 | 典型场景 |
|---|---|---|
| FATAL | 致命错误,系统无法继续运行 | 数据库连接池耗尽、核心线程崩溃 |
| ERROR | 错误,但系统可继续运行 | 报价请求超时、外部接口返回异常 |
| WARN | 警告,可能存在问题 | 报价延迟超过阈值、重试次数过多 |
| INFO | 正常业务信息 | 报价请求接收、路由决策完成 |
| DEBUG | 调试信息,仅开发环境使用 | 详细参数、中间计算结果 |
这里有个小技巧:动态调整日志级别。线上出问题时,可以通过管理接口临时把某个模块的日志级别降到DEBUG,问题排查完再调回去。这样既不影响性能,又能快速定位问题。
JSON结构化日志:让机器能读懂
传统日志长什么样?大概是这样:
2024-01-15 10:30:22.456 INFO 报价请求处理成功,订单号: ORD123456,金额: 1000.00
这种日志,人眼看看还行,但你想用ELK做分析?想用Grafana做可视化?门都没有。因为机器没法解析这种非结构化的文本。
所以,我强烈建议用JSON结构化日志。同样的信息,写成JSON:
{
"timestamp": "2024-01-15T10:30:22.456+08:00",
"level": "INFO",
"logger": "com.example.quote.QuoteService",
"message": "报价请求处理成功",
"orderId": "ORD123456",
"amount": 1000.00,
"currency": "CNY",
"duration": 45,
"sourceIp": "192.168.1.100"
}
你看,这样一搞,每个字段都是独立的key-value。Elasticsearch可以直接索引,Kibana可以随意聚合查询。想查某个订单的所有日志?orderId: ORD123456 一搜就出来了。
traceId和spanId。这样你才能把一次报价请求的完整链路串起来——从网关到路由引擎,再到下游银行接口,全链路追踪。
实现JSON日志,在Java里用Logback或Log4j2,配置一个JSON Layout就行。Python的话,用python-json-logger。代码示例:
// Logback配置示例
<appender name="JSON" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.JsonEncoder"/>
</appender>
日志采集工具:Filebeat vs Fluentd
日志写好了,怎么收集到中央存储?这就轮到采集工具上场了。市面上主流的两款:Filebeat和Fluentd。
Filebeat:轻量级选手
Filebeat是Elastic生态的采集器。特点就一个字:轻。它用Go写的,内存占用极小,适合部署在每台机器上。
我一般在报价路由系统的每个节点上都部署一个Filebeat,配置很简单:
filebeat.inputs:
- type: log
paths:
- /var/log/quote-routing/*.json
json.keys_under_root: true
json.add_error_key: true
output.elasticsearch:
hosts: ["http://elasticsearch:9200"]
index: "quote-routing-%{+yyyy.MM.dd}"
你看,几行配置就搞定了。Filebeat会自动监听日志文件变化,新日志一产生就推送到Elasticsearch。
Fluentd:全能型选手
Fluentd用Ruby写的,功能更强大,但资源消耗也大一些。它最大的优势是插件生态丰富——输入、输出、过滤,各种插件应有尽有。
举个例子,你想在采集时对日志做点处理:
<source>
@type tail
path /var/log/quote-routing/*.json
format json
tag quote.routing
</source>
<filter quote.routing>
@type record_transformer
<record>
hostname "#{Socket.gethostname}"
env "production"
</record>
</filter>
<match quote.routing>
@type elasticsearch
host elasticsearch
port 9200
logstash_format true
logstash_prefix quote-routing
</match>
这个配置里,我加了一个filter,自动给每条日志打上主机名和环境标签。这样在Kibana里就能按主机筛选,排查问题特别方便。
怎么选?
我个人建议:
- 场景简单(就是采集日志送到ES)→ 用Filebeat,省资源
- 场景复杂(需要数据转换、路由、缓冲)→ 用Fluentd,灵活
- 混合使用:Filebeat采集 + Fluentd做聚合处理,这也是我目前在用的方案
json.add_error_key: true,这样解析失败的日志会被标记,方便排查。
知识体系总览
下面这张图,把日志采集与结构化的核心逻辑串起来了:
从这张图能看出来,整个流程就是:应用生成JSON日志 → 采集器收集 → 存储到ES → 触发告警。每一步都环环相扣,缺一不可。
最后说两句
日志这件事,前期设计得好,后期运维能省80%的精力。我见过太多团队,日志随便写写,出问题的时候抓瞎。你想想看,线上报价延迟了,你连是哪个环节慢都不知道,怎么排查?
所以,别嫌麻烦。把日志级别定好,用JSON结构化输出,再配上Filebeat或Fluentd采集。这套体系搭好了,以后排查问题就跟查数据库一样简单。
- 日志级别分五档,生产环境别开DEBUG
- JSON结构化日志,必须包含traceId做链路追踪
- Filebeat轻量,Fluentd灵活,按需选择
- 采集时做好字段清洗,避免脏数据进ES