日志格式标准:Syslog、JSON、CEF、自定义格式

说到日志,第一件事是什么?不是存哪里、存多久,而是——你拿到的日志到底是什么样子的。我见过太多团队上来就谈存储方案,结果日志格式乱七八糟,解析器写了一个又一个,最后数据根本对不上。所以今天咱们先把格式这事掰扯清楚。

日志格式,说白了就是你在日志文件里看到的那一行行文字(或二进制)的结构约定。没有标准,系统间互读就是灾难。我个人习惯是:选格式前先把这三件事想清楚——谁来消费?数据量多大?字段固定还是多变?

我的经验: 曾经有个安全审计项目,对接了十几套设备,每家的日志格式都是“自定义增强版”。最后写解析规则写了一周,还老漏字段。从那以后我坚持——能用标准格式就用标准格式,除非你有足够理由。

2.1 Syslog:最老牌的文本日志

Syslog 从 Unix 时代就存在了,现在你用 Linux 服务器,/var/log/syslog 或 /var/log/messages 用的就是它。格式很简洁:时间戳 + 主机名 + 进程/模块 + 消息正文。

但你要注意:Syslog 没有强制要求消息正文的结构。所以同样是 SSH 登录失败的日志,不同设备能写出八种样子:

# 传统 Syslog(RFC 3164)
Mar 29 10:15:30 webserver sshd[1234]: Failed password for root from 192.168.1.100 port 22

# 新版 Syslog(RFC 5424,带结构化数据)
<34>1 2025-03-29T10:15:30.123Z webserver sshd 1234 - [timeQualified reason="password"][origin ip="192.168.1.100"]
Failed password for root

RFC 5424 引入了结构化数据(SD-ELEMENT),可以塞进 key-value 对。嗯,这就是进步。但现实中很多老设备只支持 RFC 3164,你解析时必须兼容两种。

避坑提醒: 我曾经碰到过一台网络设备,它的 Syslog 里 没有年份,而且时间戳用本地时间不带时区。跨年归档时时间乱得一塌糊涂。所以抓 Syslog 时,记得让设备配置为 UTC + 完整时间格式。

2.2 JSON:灵活的结构化日志

JSON 是我现在最常用的格式。为什么?因为它天然支持嵌套、数组、任意字段。你可以在一个日志事件里塞 host、user、request、response、error stack,可读性好,解析也简单。

{
  "timestamp": "2025-03-29T10:15:30.123Z",
  "level": "ERROR",
  "logger": "com.example.OrderService",
  "thread": "http-nio-8080-exec-3",
  "message": "Payment timeout for order #12345",
  "context": {
    "orderId": 12345,
    "userId": "u_998877",
    "amount": 299.99,
    "retryCount": 3,
    "targetService": "payment-gateway"
  }
}

你想想看,如果用 Syslog 写上面这条日志,消息正文就得拼成一长串,解析时还得用正则拆。但 JSON 直接拿来就能用,日志收集工具(Filebeat、Fluentd)原生支持,字段映射成本几乎为零。

但 JSON 也有坑——体积大。每条日志多了花括号、逗号、引号,数据量上涨 30%~50% 很正常。如果你的日志量每天几个 TB,那就得考虑压缩或换更紧凑的格式了。

项目经验: 我曾在某金融平台把日志从自定义文本切到 JSON。一开始大家嫌生成的日志文件膨胀太快,后来上了 .gz 压缩 + 按小时分片,问题就解决了。压缩后磁盘占用反而比原来带分隔符的文本少了 15%(因为 JSON 里重复字段名的压缩率更高)。

2.3 CEF:安全设备的最爱

CEF(Common Event Format)是 ArcSight 定义的标准,现在几乎成了安全设备日志的默认语言。防火墙、IDS/IPS、VPN 网关都会输出 CEF。为什么呢?因为 CEF 在一条日志里同时保有了必选字段的固定位置和扩展字段的灵活性。

标准格式长这样:

CEF:版本|设备厂商|设备产品|设备版本|事件类型|事件名称|严重级别|扩展字段

# 实际例子:
CEF:0|PaloAlto|PAN-OS|10.1|traffic|allow|5|src=192.168.1.10 dst=10.0.0.1 dport=443
  proto=tcp app=ssl bytes=2048

看懂了吗?前面 7 个竖线分隔的字段是固定位置,后面的扩展字段用 key=value 格式。这样既保证了解析器能快速拿到设备名、事件类型、严重级别,又不限制你想加什么额外信息。

但我得说一句,CEF 的缺点也很明显——字段名没有统一字典。A 厂商用 src,B 厂商用 sourceAddress,归一化时你得写映射表。还有,有些设备厂商在扩展字段里塞了很多自定义的“=”或空格需要转义,解析时一不留神就踩坑。

我曾经遇到的坑: 某厂商的 CEF 日志里,扩展字段的一个 value 里包含了等号:raw=user=admin&pass=***。解析器直接把它拆成了两个字段,导致后面的字段错位。后来我加了一个判断:如果 value 以双引号开头,就把它当整体直到下一个未转义的双引号。

2.4 自定义格式:什么时候用?怎么用?

上面三种都不满足你,那就只有自己造轮子了。但——你确定吗?我见过很多团队因为“不想写复杂的解析规则”就自创格式,结果后期维护成本远高于预期。

什么时候可以考虑自定义:

  • 你的业务场景非常特殊,标准格式无法表达核心字段
  • 数据量极大(比如百万条/秒),必须用极简的分隔符格式来压缩体积
  • 传递的是二进制协议,文本格式不再适用

举个例子,我曾经在 IoT 平台做过一个协议:设备ID|时间戳|槽位号|传感器类型:值|...|校验码,没有花括号没有引号,一条日志只有几十字节。解析器也简单,直接用 split('|') 就能拿到所有字段。

但自定义格式必须有版本号和文档。版本号可以放在第一个字段,方便未来升级。文档要写清楚每个字段的顺序、类型、取值范围、转义规则。否则过了半年换个人来维护,你连猜带蒙都看不懂。

我的建议: 自定义格式第一版一定要留保留字段。比如我习惯在开头加一个 1 字节的版本号,在末尾加一个 2 字节的 CRC。后期要加字段时,不需要改解析器整体结构,只需用版本号做分支。

2.5 格式对比与选择决策

下面这个表格我经常用在方案评审会上,帮你快速定位该用哪种:

特性 Syslog JSON CEF 自定义(举例)
解析复杂度 低(但消息体需要正则) 低(标准解析器) 中(字段需映射字典) 高(自己写解析器)
灵活度 低(消息体非结构化) 高(嵌套/可变字段) 中(扩展字段但无约束) 极高(可完全定制)
数据体积 小(纯文本) 大(元数据多) 中(分隔符+key=value) 极小(二进制或极简分隔符)
设备支持 几乎所有网络/安全设备 应用层、微服务 安全设备(SIEM 生态) 仅自定义系统
跨系统互操作性 高(通用标准) 高(Web 生态通用) 中(安全圈通用) 低(绑定自家系统)

你看,没有银弹。选格式实际上是在灵活度、体积、解析成本三者之间找平衡。下面的 SVG 图更直观地展示了这个决策过程:

日志格式选择决策流程 日志系统需求定义 数据量巨大? 自定义或二进制 需跨系统消费? JSON(灵活) 安全设备为主? CEF(安全) Syslog(通用) 选型依据:数据规模 × 消费系统数量 × 设备类型 × 解析成本
小技巧: 很多团队会用“双写”策略——核心链路日志写一份 JSON 用于在线排障,同时写一份紧凑的自定义格式用于离线归档。这样既保证了查问题时的可读性,又降低了长期存储成本。