4. 日志格式分析:三大主流日志的结构拆解
日志解析,说白了就是跟格式打交道。你格式拆不明白,后面写什么规则都是白搭。我这些年看过的日志格式少说也有上百种,但万变不离其宗——大部分都脱胎于三种基础类型:Web 访问日志、系统日志、应用日志。
今天咱们就把这三类日志的「骨架」拆开来看。你掌握了这个,以后遇到再奇怪的格式,也能一眼看出它的底子。
4.1 Web 访问日志:Apache 与 Nginx 的「标准答案」
Web 日志是日志解析里最常见的类型。我刚开始做运维那会儿,每天第一件事就是翻访问日志,看有没有异常请求。
4.1.1 Apache 的 Combined 格式
Apache 默认有两种格式:Common Log Format (CLF) 和 Combined Log Format。后者比前者多了 Referer 和 User-Agent,实战中基本都用 Combined。
一条典型的 Combined 日志长这样:
192.168.1.100 - frank [10/Oct/2023:13:55:36 +0800] "GET /api/users HTTP/1.1" 200 2326 "https://example.com/home" "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36"
咱们把它拆开看:
| 字段 | 示例值 | 说明 |
|---|---|---|
| 远程主机 IP | 192.168.1.100 | 客户端的 IP 地址 |
| 客户端身份 | - | identd 协议返回,通常为 - |
| 认证用户 | frank | HTTP 认证的用户名,无认证则为 - |
| 请求时间 | [10/Oct/2023:13:55:36 +0800] | 格式固定,时区信息在方括号内 |
| 请求行 | "GET /api/users HTTP/1.1" | 方法 + URI + 协议版本 |
| 状态码 | 200 | HTTP 响应状态码 |
| 响应字节数 | 2326 | 不含响应头的 body 大小 |
| Referer | "https://example.com/home" | 请求来源页面 |
| User-Agent | "Mozilla/5.0 ..." | 客户端浏览器/工具标识 |
4.1.2 Nginx 的默认格式
Nginx 的默认格式跟 Apache 的 Combined 几乎一样,但有个关键区别——时间格式。Nginx 的时间不带方括号,而且时区格式不同。
192.168.1.100 - frank [10/Oct/2023:13:55:36 +0800] "GET /api/users HTTP/1.1" 200 2326 "https://example.com/home" "Mozilla/5.0" # Apache
192.168.1.100 - frank 10/Oct/2023:13:55:36 +0800 "GET /api/users HTTP/1.1" 200 2326 "https://example.com/home" "Mozilla/5.0" # Nginx
看出来了吗?Nginx 的时间字段外面没有方括号。这个细节坑过不少人——包括我。有一次我写了个解析脚本,直接套用 Apache 的格式,结果 Nginx 日志全解析失败,排查了半天才发现是方括号的问题。
4.1.3 自定义日志格式
生产环境里,很少有人用默认格式。我见过最夸张的一次,一个团队在日志里塞了 30 多个字段,包括上游响应时间、缓存命中状态、SSL 协议版本等等。
Nginx 自定义格式示例:
log_format extended '$remote_addr - $remote_user [$time_local] '
'"$request" $status $body_bytes_sent '
'"$http_referer" "$http_user_agent" '
'$request_time $upstream_response_time '
'$ssl_protocol $ssl_cipher';
对应的日志:
192.168.1.100 - - [10/Oct/2023:13:55:36 +0800] "POST /order HTTP/2.0" 201 128 "https://example.com/cart" "curl/7.68.0" 0.032 0.028 TLSv1.3 TLS_AES_256_GCM_SHA384
4.2 系统日志:Syslog 的「三要素」
系统日志跟 Web 日志完全是两套逻辑。Web 日志关注的是「谁、什么时候、干了什么」,而系统日志关注的是「哪个程序、什么级别、说了什么」。
4.2.1 Syslog 的标准结构
一条标准的 Syslog 包含三个核心部分:
<13>Oct 10 13:55:36 myhost sshd[1234]: Failed password for root from 192.168.1.100 port 22 ssh2
拆解如下:
- 优先级(PRI):
<13>,由设施(Facility)和级别(Severity)计算得出。13 = 1*8 + 5,即 Facility=1(user-level),Severity=5(notice)。 - 时间戳:
Oct 10 13:55:36,注意没有年份,这是 Syslog 的老传统。 - 主机名:
myhost,发送日志的设备名称。 - 进程信息:
sshd[1234],进程名和 PID。 - 消息体:剩余部分,具体的事件描述。
4.2.2 Facility 与 Severity 的对应关系
这两个概念是 Syslog 的灵魂。Facility 告诉你是哪个子系统,Severity 告诉你有多严重。
| Facility 值 | 关键字 | 说明 |
|---|---|---|
| 0 | kern | 内核消息 |
| 1 | user | 用户级消息 |
| 3 | daemon | 系统守护进程 |
| 4 | auth | 安全/认证消息 |
| 5 | syslog | syslog 内部消息 |
| 16-23 | local0-local7 | 本地自定义使用 |
| Severity 值 | 关键字 | 说明 |
|---|---|---|
| 0 | emerg | 系统不可用 |
| 1 | alert | 必须立即处理 |
| 2 | crit | 严重状态 |
| 3 | err | 错误 |
| 4 | warning | 警告 |
| 5 | notice | 正常但重要 |
| 6 | info | 信息 |
| 7 | debug | 调试 |
核心思路:解析 Syslog 时,我习惯先把 PRI 值拆出来,通过位运算拿到 Facility 和 Severity。这样后面做告警分级就方便了——比如 Severity 小于 3 的,直接进 PagerDuty。
4.2.3 现代 Syslog 的变体
嗯,这里要注意。传统的 Syslog 格式太老了,很多现代系统开始用 RFC 5424 格式。它长这样:
<13>1 2023-10-10T13:55:36.123Z myhost sshd 1234 - [timeQuality tzKnown="1" isSynced="1"] Failed password for root
区别很明显:时间用了 ISO 8601 格式,加了版本号,还多了结构化数据段。解析的时候,我建议直接用正则把 PRI、版本、时间、主机、进程、PID、消息体这几个字段分别提取出来。
4.3 应用日志:最「自由」也最「混乱」的类型
应用日志是三类日志里最让人头疼的。为什么?因为没有统一标准。每个开发团队都有自己的「审美」。
4.3.1 常见应用日志格式
我归纳了一下,应用日志大概有三种主流风格:
- 纯文本风格:时间 + 级别 + 消息,简单粗暴。
- JSON 风格:结构化,机器友好,但可读性差。
- 混合风格:前面是固定字段,后面是自由文本。
举个例子:
# 纯文本
2023-10-10 13:55:36.123 ERROR [http-nio-8080-exec-1] com.example.OrderService: Failed to create order for user 12345
# JSON
{"timestamp":"2023-10-10T13:55:36.123Z","level":"ERROR","logger":"com.example.OrderService","thread":"http-nio-8080-exec-1","message":"Failed to create order","userId":12345,"duration":1523}
# 混合
2023-10-10 13:55:36.123 | ERROR | http-nio-8080-exec-1 | c.e.OrderService | Failed to create order | userId=12345 duration=1523ms
4.3.2 解析策略:先分类,再拆解
面对应用日志,我的策略很简单:先判断格式类型,再选择解析方式。
4.4 三种日志的核心差异对比
咱们用一张表来总结:
| 维度 | Web 访问日志 | 系统日志 (Syslog) | 应用日志 |
|---|---|---|---|
| 标准化程度 | 高(Combined 格式为主) | 中(RFC 3164/5424) | 低(无统一标准) |
| 核心字段 | IP、时间、请求、状态码 | PRI、时间、主机、进程 | 时间、级别、类名、消息 |
| 时间格式 | [dd/MMM/yyyy:HH:mm:ss Z] | MMM dd HH:mm:ss 或 ISO 8601 | 多种,常见 yyyy-MM-dd HH:mm:ss.SSS |
| 解析难度 | 低(格式固定) | 中(PRI 需要计算) | 高(格式多变) |
| 典型用途 | 流量分析、安全审计 | 系统监控、故障排查 | 业务分析、错误追踪 |
4.5 日志格式拆解的整体框架
说了这么多,咱们用一张图把整个知识体系串起来。这是我做日志解析时脑子里一直挂着的框架:
这张图我建议你存下来。每次拿到一种新日志,先往这个框架里套一套——属于哪类?什么格式?核心字段有哪些?解析策略怎么选?走完这三步,你的解析规则基本就成型了。
一句话总结:日志格式拆解的本质,就是找到「不变」的骨架,应对「万变」的细节。Web 日志看请求行,Syslog 看 PRI,应用日志看时间戳——抓住这三个锚点,解析规则就不会跑偏。