2. 日志格式规范:通用格式、JSON结构化与日志级别
日志格式这事,看着简单,但坑特别多。我见过太多团队一开始随便写几行日志,等到要排查线上问题时,才发现格式乱七八糟,grep 都救不了你。说白了,日志格式就是你和系统之间的「共同语言」,语言不通,啥都查不出来。
这一节我们聊聊三种主流格式:Apache/Nginx 的通用日志格式、JSON 结构化日志,以及日志级别怎么定。嗯,都是实战中绕不开的东西。
2.1 通用日志格式(Apache/Nginx)
先说说最经典的通用日志格式。Apache 和 Nginx 默认的日志格式,业内叫 Combined Log Format(CLF)。你肯定见过:
192.168.1.10 - - [10/Oct/2023:13:55:36 +0800] "GET /api/users HTTP/1.1" 200 2326 "https://example.com" "Mozilla/5.0"
这个格式包含:客户端 IP、时间戳、请求方法+路径+协议、状态码、响应大小、Referer、User-Agent。我在项目中遇到过,直接用 Nginx 默认格式上线,结果发现时间戳没有时区信息,跨地域排查时对时间对到崩溃。
log_format 自定义一下,把时区、响应时间、上游地址都加进去。别偷懒用默认。
Nginx 自定义示例:
log_format extended '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'$request_time $upstream_addr';
access_log /var/log/nginx/access.log extended;
通用格式的优点是人眼可读,但缺点也很明显——机器解析效率低。你想想看,用 awk 切字段时,如果某个字段里多了个空格,整行就废了。所以,我一般只在开发环境或临时调试时用这种格式。
2.2 JSON 结构化日志
到了生产环境,我个人强烈推荐 JSON 结构化日志。为什么?因为机器解析友好,字段可扩展,而且跟日志收集系统(ELK、Loki)天然契合。
JSON 日志长这样:
{
"timestamp": "2023-10-10T13:55:36.123+08:00",
"level": "INFO",
"logger": "com.example.UserController",
"thread": "http-nio-8080-exec-3",
"message": "用户查询成功",
"context": {
"userId": "10086",
"requestId": "a1b2c3d4",
"duration": 45
}
}
你看,每个字段都有明确的含义,而且可以嵌套。我曾经接手过一个老项目,日志全是字符串拼接,查一次接口调用链要翻 5 个文件。后来改成 JSON 格式,配合 Elasticsearch,点几下就定位到问题。
timestamp:ISO 8601 格式,带时区level:日志级别logger:来源类或模块名traceId:链路追踪 ID(后面章节会细讲)message:人类可读的描述context:业务上下文(用户ID、订单号等)
Nginx 输出 JSON 日志也不难:
log_format json_combined escape=json
'{'
'"timestamp":"$time_iso8601",'
'"remote_addr":"$remote_addr",'
'"request":"$request",'
'"status":$status,'
'"body_bytes_sent":$body_bytes_sent,'
'"request_time":$request_time,'
'"upstream_addr":"$upstream_addr",'
'"referer":"$http_referer",'
'"user_agent":"$http_user_agent"'
'}';
access_log /var/log/nginx/access.log json_combined;
2.3 日志级别定义
日志级别这事,看起来就是 TRACE、DEBUG、INFO、WARN、ERROR 五个词,但实际用起来,每个团队的理解都不一样。我见过有人把 INFO 当 DEBUG 用,一天刷几个 G 的日志;也见过有人只在 ERROR 里打日志,出了问题啥线索都没有。
下面是我个人在项目中沉淀下来的级别定义,供你参考:
| 级别 | 含义 | 使用场景 | 生产环境是否开启 |
|---|---|---|---|
| TRACE | 细粒度追踪 | 调试特定代码路径、方法入参出参 | 否(除非临时排查) |
| DEBUG | 调试信息 | 开发环境排查逻辑、变量值 | 否 |
| INFO | 关键业务事件 | 请求开始/结束、用户操作、定时任务执行 | 是 |
| WARN | 潜在问题 | 配置降级、重试、限流触发、非关键异常 | 是 |
| ERROR | 错误事件 | 数据库连接失败、接口返回 500、业务异常 | 是 |
这里有个容易踩的坑:INFO 和 DEBUG 的边界。我曾经在一个项目中,把每次 SQL 查询都打成了 INFO,结果每天日志量 50GB,磁盘直接爆了。后来我定了个规则:INFO 只记录业务层面的「事件」,比如「用户下单成功」;DEBUG 才记录技术细节,比如「SQL 执行耗时 23ms」。
2.4 本章知识体系图
下面这张图把日志格式规范的核心脉络梳理了一下,你可以对照着理解:
2.5 小结
日志格式这事,选对了事半功倍,选错了天天填坑。我个人建议:
- 开发环境:用通用格式,方便人眼看
- 测试/预发环境:用 JSON 格式,开始对接日志系统
- 生产环境:必须 JSON 结构化,级别控制在 INFO 及以上