3、日志采集基础:日志格式规范、日志级别与切割策略
日志这东西,说白了就是系统的「黑匣子」。你想想看,线上出了故障,第一反应是不是先翻日志?我做了这么多年运维,见过太多团队因为日志不规范,排查问题像大海捞针。今天咱们就把日志的底子打扎实。
3.1 日志格式规范:JSON 还是纯文本?
这是个老生常谈的问题。我个人习惯,生产环境一律用 JSON 格式。为什么?因为机器好解析。
纯文本日志长这样:
2025-01-15 10:23:45 ERROR 用户下单失败,订单号: ORD20250115001,原因: 库存不足
看着挺清楚对吧?但你要用 Logstash 或者 Fluentd 去解析它,就得写正则表达式。字段一多,正则就变成天书了。我在项目中遇到过,一个正则写错,整条日志解析失败,数据全丢了。
JSON 格式就好很多:
{
"timestamp": "2025-01-15T10:23:45.123+08:00",
"level": "ERROR",
"logger": "com.example.OrderService",
"thread": "http-nio-8080-exec-3",
"message": "用户下单失败",
"orderId": "ORD20250115001",
"reason": "库存不足",
"userId": 10086,
"duration": 235
}
你看,字段名、字段类型一目了然。采集端拿到 JSON,直接按 key 取值就行,省心省力。
核心原则:日志格式要「机器可读优先,人类可读其次」。JSON 是当前最通用的选择。
3.2 日志字段设计:该记什么?
不是所有信息都往日志里塞。我见过有人把整个 HTTP 请求体都打出来,结果一天日志量几个 TB,磁盘直接爆了。嗯,这里要注意,日志字段要精打细算。
我建议至少包含以下字段:
| 字段 | 类型 | 说明 | 是否必填 |
|---|---|---|---|
| timestamp | ISO8601 字符串 | 日志产生时间,带时区 | 是 |
| level | 字符串 | 日志级别:DEBUG/INFO/WARN/ERROR | 是 |
| logger | 字符串 | 产生日志的类名或模块名 | 是 |
| thread | 字符串 | 线程名,方便追踪并发问题 | 推荐 |
| message | 字符串 | 日志正文,简明扼要 | 是 |
| traceId | 字符串 | 分布式链路追踪 ID | 推荐 |
| context | 对象 | 业务上下文,如 userId、orderId | 按需 |
| duration | 整数(ms) | 接口或操作耗时 | 按需 |
小技巧:timestamp 一定要用 ISO8601 格式带时区,比如
2025-01-15T10:23:45.123+08:00。别用时间戳毫秒数,排查问题时谁去换算?
3.3 日志级别:别什么都打 ERROR
日志级别是个老话题,但很多人用不好。我见过最离谱的,有人把「用户查询商品列表」这种正常操作也打 INFO,一天下来 INFO 日志几千万条,真正有用的 ERROR 反而被淹没了。
我的建议是这样的:
- FATAL:系统即将崩溃,比如数据库连接池耗尽、磁盘空间不足。这种级别应该触发告警,直接打电话给值班人员。
- ERROR:请求处理失败,但系统还能继续运行。比如用户下单失败、第三方接口超时。需要人工介入排查。
- WARN:潜在问题,但当前请求还能正常返回。比如配置项缺失使用默认值、接口响应时间超过阈值。
- INFO:关键业务流程节点。比如用户注册成功、订单状态变更。别把循环里的每次迭代都打 INFO。
- DEBUG:开发阶段调试用。线上环境建议关闭,或者按需动态开启。
- TRACE:比 DEBUG 更细的粒度,一般只在定位疑难问题时临时开启。
避坑指南:我曾经在一个项目中,同事把数据库查询异常打成了 WARN 级别。结果线上数据库挂了,告警系统没触发,等我们发现时已经过去半小时。记住:凡是需要人工处理的,至少打 ERROR。
3.4 日志切割策略:别让日志撑爆磁盘
日志如果不切割,一天下来一个文件几个 GB,打开都费劲。更别说采集了。切割策略说白了就两个维度:按时间和按大小。
常见的切割方案:
| 策略 | 配置示例 | 适用场景 |
|---|---|---|
| 按天切割 | 每天 00:00 生成新文件 | 日志量稳定,适合长期归档 |
| 按小时切割 | 每小时生成一个新文件 | 日志量大,需要快速定位 |
| 按大小切割 | 单个文件超过 100MB 就切 | 日志量波动大,防止单文件过大 |
| 混合策略 | 按小时 + 最大 500MB | 生产环境推荐,双重保险 |
以 Java 的 Logback 为例,配置一个混合策略:
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>/var/log/app/app.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>/var/log/app/app.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<maxHistory>30</maxHistory>
</rollingPolicy>
</appender>
这个配置的意思是:按天切割,但如果当天日志超过 100MB,就再切一个文件。保留最近 30 天的日志。
经验之谈:切割后的日志文件名一定要带时间戳。我见过有人用
app.log.1、app.log.2 这种命名,结果采集器不知道哪个是最新的,乱套了。
3.5 知识体系总览
下面这张图把日志采集基础的核心知识点串起来了,你可以对照着梳理自己的日志体系:
3.6 实战建议:从零开始怎么搭?
如果你现在要从零开始,我建议按这个顺序来:
- 先定格式:统一用 JSON,定义好字段模板。团队内部拉个会,把字段名对齐。
- 再定级别:明确什么情况打什么级别。最好写进开发规范里,代码 Review 时重点检查。
- 最后配切割:根据预估日志量,选好切割策略。初期可以按天 + 按大小混合,后期再调。
一句话总结:日志格式要统一、级别要分明、切割要自动。这三件事做好了,后面的采集、存储、分析才能顺起来。