第3章:Syslog消息格式详解 —— PRI、HEADER、MSG

搞懂Syslog的消息格式,是审计日志路上的第一道坎。我自己刚接触那会儿,对着一条日志看了半天,愣是不知道哪部分是优先级,哪部分是时间戳。后来才明白,整个消息就是三块拼起来的。

说白了,Syslog消息的格式长这样:

<PRI>HEADER MSG

嗯,就这么简单。但每块里面藏着不少细节。我带你一个一个拆开看。

3.1 PRI —— 优先级,藏在尖括号里的两个关键参数

PRI 字段用尖括号包裹,比如 <134>。它不是一个数,而是两个值的组合:设备(Facility)和严重级别(Severity)。计算公式是:

PRI = Facility × 8 + Severity

举个例子。我之前排查一台 Cisco 路由器,日志里出现 <134>,算一下:134 ÷ 8 = 16 余 6。Facility = 16(Local0),Severity = 6(Informational)。嗯,这就是 Local0 设备发来的信息日志。

Facility 和 Severity 都有标准定义,我整理了一个常用表格:

代码 Facility 名称 说明
0kern内核消息
1user用户级消息
2mail邮件系统
3daemon系统守护进程
4auth安全/认证消息
5syslogsyslogd 内部消息
6lpr行打印机子系统
7news网络新闻子系统
8uucpUUCP 子系统
9cron时钟守护进程
10authpriv安全/认证消息(私有)
11ftpFTP 守护进程
16–23local0–local7本地自定义
代码 Severity 名称 说明
0Emergency系统不可用
1Alert必须立即处理
2Critical严重条件
3Error错误条件
4Warning警告条件
5Notice正常但重要条件
6Informational信息消息
7Debug调试级消息
技巧:你可以在接收端用 PRI 做过滤。比如我只关心 severity ≤ 3(Error 及以上)的日志,直接按数值判断就行,不用解析字符串。

3.2 HEADER —— 时间戳和主机名

HEADER 部分紧跟在 PRI 后面,包含两个信息:时间戳和主机名,中间用空格隔开。啊对了,典型格式是:

Oct  1 12:34:56 myhost 

时间戳:RFC 3164 规定是 Mmm dd hh:mm:ss,不带年份。月份缩写为英文前三个字母(Jan, Feb, ... Dec)。日期如果是个位数,前面补一个空格(不是补0)。

你可能会觉得奇怪,为什么没有年份?嗯,这是历史遗留问题。我在项目里对接过一些老设备,传过来的时间戳全是本地时间,也不带时区。这导致跨时区审计时特别痛苦。所以我建议:日志传输链路里尽量统一成 UTC,接收端再转成当地时间。

主机名:一般是源设备的主机名或 IP 地址。有些设备会直接用 FQDN,有些只用短名。注意:主机名里不能有空格,如果有空格会被解析成后续字段。

踩坑记录:我曾经遇到一台防火墙,主机名里带了下划线,结果 rsyslog 解析时把下划线当成分隔符,日志内容全乱了。检查了半天才发现是主机名的问题。建议主机名只使用字母、数字和连字符。

3.3 MSG —— 消息体,包含 TAG 和 Content

MSG 字段从 HEADER 之后开始,直到行尾。它分为两部分:TAG 和 Content。TAG 通常是进程名或应用标识,后面紧跟一个冒号或方括号,然后是消息内容。

举个例子:

<134>Oct  1 12:34:56 myhost sshd[12345]: Failed password for root from 10.0.0.1 port 22

这里面 sshd[12345] 就是 TAG,冒号后面的全是 Content。TAG 的格式灵活,常见的包括:

  • 进程名[PID]:如 sshd[12345],PID 可选
  • 应用名称:如 sudo:、kernel:
  • 设备标识:有些网络设备直接用模块名

Content 部分没有严格限制,可以是任意文本。但实际中很多厂商遵守 IETF RFC 5424 的结构化数据格式,比如用 [data] 包裹键值对。不过这是后话了,我们后面章节会讲 RFC 5424。

3.4 整条消息的视觉拆解

上面这些概念,我们用一张图串起来。下面是我画的 Syslog 消息结构图,把 PRI、HEADER、MSG 以及它们内部的子字段都标出来了。

Syslog 消息结构(RFC 3164) PRI HEADER MSG Facility Severity Timestamp Hostname TAG Content 示例消息拆解: <134>Oct 1 12:34:56 myhost sshd[12345]: Failed password for root from 10.0.0.1 └PRI┘└───── HEADER ─────┘└─────── MSG ───────────────┘ PRI=134 → Facility=16(Local0), Severity=6(Informational) TAG=sshd[12345] Content=Failed password for ...

3.5 实战中容易忽略的细节

  • PRI 必须紧接消息开头,前面不能有空格。我见过一些脚本生成的日志,开头加了个换行符,导致 syslogd 直接丢弃整条消息。
  • 时间戳的月份缩写大小写敏感?其实大小写都可以,但标准中第一个字母大写(Oct),其余小写。如果发来全大写的 OCT,有些接收器会解析失败。
  • 主机名后面的空格:HEADER 结束后的第一个空格就是 MSG 的开始。如果你用程序拼接消息,务必注意空格数量。多个空格会导致 MSG 开头多出空白。
  • MSG 的 TAG 里冒号:有些应用没有冒号,直接用空格分隔。这时候解析器会把整个 MSG 当成 Content,TAG 为空。你在写接收端时要做兼容。
避坑总结:解析 Syslog 时不要硬编码位置,最好按空格切分后根据尖括号、时间戳特征做正则匹配。我在一个项目中,设备日志的时间戳格式是“MM/dd/yyyy HH:mm:ss”,完全不符合 RFC 3164,最后只能用正则硬怼。所以灵活点,别死板。

3.6 你动手试一下

拿一条真实的日志,比如:

<13>Jan 15 08:30:00 webserver nginx: 10.0.0.2 - - [15/Jan/2025:08:30:00 +0000] "GET /index.html" 200 1234

自己拆一下:PRI 是多少?Facility 和 Severity 分别是多少?HEADER 里时间戳和主机名是什么?TAG 和 Content 分别对应哪段?

嗯,拆完后你会发现,其实这条消息的 nginx: 就是 TAG,后面那一长串 Access Log 格式就是 Content。虽然 Content 里还嵌了一个方括号时间戳,但那已经是应用层的事了,Syslog 本身不关心。

这一章的内容就是这些。格式弄清楚了,后面讲传输和接收的时候,你就能快速定位问题。记住三要素:PRI 决定路由,HEADER 提供上下文,MSG 承载事件。