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 调试信息 程序内部状态
注意: Syslog 的时间戳不带年份!如果你要跨年分析日志,一定要确认日志轮转策略。我曾经因为没注意这个,分析 1 月份的日志时把去年 12 月的数据也算进去了,结果统计全错。

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。不然写解析规则的时候你会想骂人。

个人经验: 在 Java 项目里,我习惯用 Logback 的 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

自定义格式的几个原则:

  1. 分隔符要稳定:别今天用管道符,明天用逗号,后天用空格
  2. 固定字段放前面:时间、级别、追踪 ID 这些通用字段放前面,业务字段放后面
  3. 避免歧义:如果字段值本身可能包含分隔符,记得转义或加引号
踩坑记录: 我曾经见过一个团队,自定义格式里用空格分隔字段,结果某个字段值里也有空格,解析直接炸了。后来改成管道符 + 字段值用双引号包裹才解决。所以,分隔符一定要选日志内容里不会出现的字符。

2.5 格式对比与选择建议

不同的场景适合不同的格式。我整理了一张对比表,方便你选型:

格式 可读性 解析效率 扩展性 适用场景
Apache/Nginx 格式 中 高 低 Web 访问日志
Syslog 中 高 低 系统日志、网络设备
JSON 高 中 高 应用日志、微服务
自定义格式 取决于设计 取决于设计 高 特定业务需求

我个人建议:新项目优先用 JSON 格式。虽然解析效率比纯文本略低,但可读性和扩展性带来的好处远大于那点性能损耗。老项目如果改造成本高,至少保证格式统一,别一个系统里混着好几种格式。

日志格式解析知识体系 日志格式解析 Apache / Nginx Syslog JSON 自定义格式 LogFormat CustomLog log_format access_log PRI 优先级 Severity 级别 结构化字段 嵌套上下文 分隔符设计 字段顺序 选型建议 新项目优先 JSON → 可读性高、扩展性好 老项目保持统一 → 避免多种格式混用