2、日志格式解析:Apache/Nginx日志格式、Syslog格式、JSON日志、自定义日志格式
日志格式这东西,说白了就是日志的「语法」。你语法都看不懂,怎么分析?
我刚开始做运维那会儿,对着满屏的日志发懵。后来被老大骂了几次,才老老实实把各种格式啃下来。今天咱们就把常见的几种格式掰开揉碎讲清楚。
2.1 Apache/Nginx 日志格式
Web 服务器的日志,是定位模块最常打交道的数据源。Apache 和 Nginx 虽然都是 Web 服务器,但日志格式的配置方式略有不同。
2.1.1 Apache 日志格式
Apache 通过 LogFormat 指令定义格式,用 CustomLog 指定输出文件。我个人习惯把格式定义放在 httpd.conf 的全局段,方便统一管理。
最常见的组合日志格式(Combined Log Format)长这样:
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
CustomLog logs/access_log combined
各字段含义:
| 占位符 | 含义 | 示例值 |
|---|---|---|
%h |
客户端 IP | 192.168.1.100 |
%l |
远程登录名(通常为 -) | - |
%u |
认证用户名(未认证则为 -) | admin |
%t |
请求时间 | [10/Oct/2023:13:55:36 +0800] |
%r |
请求行(方法+路径+协议) | GET /index.html HTTP/1.1 |
%>s |
最终状态码 | 200 |
%b |
响应体大小(字节) | 1234 |
%{Referer}i |
Referer 请求头 | https://google.com |
%{User-Agent}i |
用户代理 | Mozilla/5.0 ... |
%D(微秒级响应时间)和 %T(秒级响应时间)来排查慢请求。我在一次大促压测中,就是靠 %D 揪出了一个响应耗时 3 秒的接口。
2.1.2 Nginx 日志格式
Nginx 的配置更灵活,通过 log_format 定义,access_log 引用。嗯,这里要注意:Nginx 的变量名前面要加 $ 符号。
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
跟 Apache 对比一下,你会发现核心字段几乎一样。但 Nginx 多了一个 $http_x_forwarded_for,这个在反向代理场景下特别有用——能拿到真实的客户端 IP。
避坑指南: 我曾经在 Nginx 后面挂了 CDN,结果 access_log 里全是 CDN 节点的 IP。后来加上 $http_x_forwarded_for 才拿到真实用户 IP。如果你也遇到类似情况,记得检查日志格式里有没有这个字段。
2.2 Syslog 格式
Syslog 是 Linux 系统日志的标准格式,几乎所有系统组件(内核、sshd、cron)都往这里写日志。你想想看,定位系统级问题,第一站就是翻 /var/log/syslog 或 /var/log/messages。
标准的 Syslog 格式长这样:
<PRI>TIMESTAMP HOSTNAME APPNAME[PID]: MESSAGE
举个例子:
<34>Oct 10 14:23:45 webserver sshd[12345]: Failed password for root from 192.168.1.200 port 22 ssh2
拆解一下:
- <34>:优先级(Priority),由设施(Facility)和严重级别(Severity)计算得出。34 = 4(auth 设施)× 8 + 2(crit 级别)
- Oct 10 14:23:45:时间戳,不带年份
- webserver:主机名
- sshd[12345]:进程名和 PID
- Failed password ...:具体消息内容
Syslog 的严重级别从 0(Emergency)到 7(Debug),我一般重点关注 0-3 级别的日志——这些通常意味着系统出大事了。
| 级别 | 数值 | 含义 | 常见场景 |
|---|---|---|---|
| Emergency | 0 | 系统不可用 | 内核崩溃 |
| Alert | 1 | 必须立即处理 | 磁盘损坏 |
| Critical | 2 | 严重错误 | SSH 爆破攻击 |
| Error | 3 | 错误 | 服务启动失败 |
| Warning | 4 | 警告 | 磁盘使用率 90% |
| Notice | 5 | 正常但重要 | 用户登录成功 |
| Info | 6 | 一般信息 | 服务启动 |
| Debug | 7 | 调试信息 | 程序内部状态 |
2.3 JSON 日志
JSON 日志是现在微服务架构的标配。为什么?因为结构清晰,程序解析方便,而且可以嵌套复杂数据。
一个典型的 JSON 日志长这样:
{
"timestamp": "2023-10-10T14:23:45.123Z",
"level": "ERROR",
"logger": "com.example.OrderService",
"thread": "http-nio-8080-exec-3",
"message": "订单创建失败",
"context": {
"orderId": "ORD20231010001",
"userId": 12345,
"amount": 299.99
},
"stackTrace": "java.lang.RuntimeException: ..."
}
JSON 日志的好处:
- 字段可扩展:随时加字段,不影响解析
- 支持嵌套:可以把上下文信息放在一起
- 工具友好:Elasticsearch、Logstash 直接吃 JSON
我建议团队统一 JSON 日志的字段命名规范。比如时间戳统一用 timestamp,级别用 level,不要有的人用 lvl,有的人用 severity。不然写解析规则的时候你会想骂人。
JsonLayout 或者 Log4j2 的 JsonTemplateLayout,配置简单,输出格式统一。Python 项目则用 python-json-logger,几行代码就能搞定。
2.4 自定义日志格式
很多时候,现成的格式满足不了需求。比如你需要在日志里记录用户会话 ID、请求追踪 ID、或者业务特定的指标。这时候就得自己定义格式。
我举个例子。假设我们有个支付系统,需要在日志里记录:
2023-10-10 14:23:45.123 | INFO | traceId=abc123 | userId=456 | action=payment | amount=99.99 | status=success | duration=234ms
这种格式用管道符分隔,字段清晰,grep 和 awk 都好处理。解析规则也很简单:
# 用 awk 按管道符分割
awk -F ' \\| ' '{print $1, $3, $5}' payment.log
自定义格式的几个原则:
- 分隔符要稳定:别今天用管道符,明天用逗号,后天用空格
- 固定字段放前面:时间、级别、追踪 ID 这些通用字段放前面,业务字段放后面
- 避免歧义:如果字段值本身可能包含分隔符,记得转义或加引号
2.5 格式对比与选择建议
不同的场景适合不同的格式。我整理了一张对比表,方便你选型:
| 格式 | 可读性 | 解析效率 | 扩展性 | 适用场景 |
|---|---|---|---|---|
| Apache/Nginx 格式 | 中 | 高 | 低 | Web 访问日志 |
| Syslog | 中 | 高 | 低 | 系统日志、网络设备 |
| JSON | 高 | 中 | 高 | 应用日志、微服务 |
| 自定义格式 | 取决于设计 | 取决于设计 | 高 | 特定业务需求 |
我个人建议:新项目优先用 JSON 格式。虽然解析效率比纯文本略低,但可读性和扩展性带来的好处远大于那点性能损耗。老项目如果改造成本高,至少保证格式统一,别一个系统里混着好几种格式。