日志格式,你真的看懂了吗?
做日志分析这些年,我最大的感触就是——格式决定效率。
你想想看,同样是记录一条登录失败。有的日志写了一大段话,你得逐字找关键词。有的用JSON一展开,字段清清楚楚。还有的CEF格式,安全设备专用,看一眼就能知道攻击类型。
我个人习惯是:先看懂格式,再写解析规则。不然你正则写得再漂亮,字段都找不对位置,白搭。
3.1 Syslog:老牌通用格式
Syslog,说白了就是日志的“基本款”。从Unix时代就在用,现在几乎所有网络设备和Linux系统都在输出。
它有个特点——不强制结构。也就是说,不同设备写的Syslog,字段位置可能都不一样。这让我吃过不少苦头。
标准Syslog长什么样?
<14>Apr 8 10:15:30 webserver sshd[1234]: Failed password for root from 192.168.1.100 port 22 ssh2
拆开来看,其实是这么几段:
- <14>:优先级码。14 = 设施值(1,user-level) × 8 + 严重性(6,informational)
- Apr 8 10:15:30:时间戳。注意,不带年份!
- webserver:主机名
- sshd[1234]:进程名[进程ID]
- Failed password...:消息体。这里才是真正需要关注的内容
Syslog的设施和严重性
这个表我建议你存一份。写解析规则时,经常要根据设施值判断日志来源。
| 设施码 | 设施名称 | 说明 |
|---|---|---|
| 0 | kern | 内核消息 |
| 1 | user | 用户级消息 |
| 2 | 邮件系统 | |
| 3 | daemon | 系统守护进程 |
| 4 | auth | 安全/认证消息 |
| 5 | syslog | syslogd内部消息 |
| 16-23 | local0 - local7 | 用户自定义设备 |
严重性的话,从0到7递减:
- 0 - Emergency:系统不可用
- 1 - Alert:必须立即处理
- 2 - Critical:严重错误
- 3 - Error:错误
- 4 - Warning:警告
- 5 - Notice:正常但重要的事件
- 6 - Informational:提示信息
- 7 - Debug:调试信息
3.2 JSON格式:结构化日志的代表
JSON格式现在越来越流行。为什么?因为好解析、好查询、好扩展。
我举个例子你就明白了。同样一条登录失败,JSON格式这么写:
{
"timestamp": "2025-04-08T10:15:30.123Z",
"host": "webserver",
"process": "sshd",
"pid": 1234,
"event": "login_failed",
"user": "root",
"source_ip": "192.168.1.100",
"source_port": 22,
"protocol": "ssh",
"severity": "warning"
}
你看,字段名、字段值、数据类型,全都明明白白。写解析规则时,直接用键名取就行了,不用跟正则较劲。
JSON日志的核心字段
我个人习惯是,不管日志内容多复杂,几个核心字段必须有:
- @timestamp:时间戳,最好带时区和毫秒
- host 或 source:来源主机
- event 或 action:事件类型
- severity 或 level:严重级别
- message:原始消息(可选,但建议保留)
为什么强调message字段?我记得有一次,Elasticsearch的字段映射冲突,导致一些日志索引失败。还好每条日志都保留了原始message,我能从那里找回丢失的数据。
嵌套与扁平,怎么选?
JSON支持嵌套结构。比如:
{
"client": {
"ip": "192.168.1.100",
"port": 22,
"user": "root"
}
}
但这个在解析时有个坑——某些查询引擎对嵌套字段的索引效率不高。我建议你尽量用扁平结构,字段名用点号分隔,比如:
{
"client.ip": "192.168.1.100",
"client.port": 22,
"client.user": "root"
}
3.3 CEF格式:安全设备的专属语言
CEF是ArcSight推出的安全日志格式。全称Common Event Format。
它最大的价值在于——让不同品牌的安全设备能说同一种语言。防火墙、IDS/IPS、WAF、AV,都能用CEF统一上报。
CEF日志的结构
CEF:0|Security|WebGateway|1.0.2|100|SQL Injection Attack|5|src=203.0.113.5 dst=10.0.0.1 request=/login?id=1' OR '1'='1
先拆开看看:
- CEF:0:版本号,目前固定为0
- Security:设备厂商
- WebGateway:设备产品
- 1.0.2:设备版本
- 100:事件ID(每类攻击有唯一ID)
- SQL Injection Attack:事件名称
- 5:严重级别(0-10,越高越严重)
- 后面全是扩展字段:key=value对,用空格分隔
这里有个容易搞混的地方——CEF的严重级别是0到10,数值越大越严重。跟Syslog的0-7刚好反过来。我刚开始对接时就在这栽过跟头。
CEF的核心扩展字段
| 字段名 | 含义 | 示例 |
|---|---|---|
| src | 源IP | 203.0.113.5 |
| dst | 目的IP | 10.0.0.1 |
| spt | 源端口 | 12345 |
| dpt | 目的端口 | 80 |
| proto | 协议 | tcp |
| request | 请求内容 | /login?id=1' OR '1'='1 |
| msg | 消息描述 | SQL注入攻击已拦截 |
| duser | 目的用户 | admin |
| dhost | 目的主机 | webserver.local |
3.4 三种格式对比:一眼看懂怎么选
| 特性 | Syslog | JSON | CEF |
|---|---|---|---|
| 结构化程度 | 低(自由文本) | 高(键值对) | 中(固定头+扩展键值) |
| 解析难度 | 高(靠正则猜字段) | 低(直接取键) | 中(需区分头与扩展) |
| 扩展性 | 差(加字段得改所有解析) | 好(新增键不影响旧解析) | 好(扩展字段自由定义) |
| 人类可读性 | 高(但字段不明确) | 中(括号多) | 中(管道分隔) |
| 适用场景 | Linux/网络设备基础日志 | Web应用/云服务/自定义日志 | 安全设备/SIEM系统集成 |
你可能会问,那实际工作中到底用哪个?
我的经验是——看你的下游系统。如果对接的是ELK,JSON最友好。如果对接的是SIEM(比如Splunk、QRadar),CEF是标准答案。如果只是本地排查,Syslog凑合能用,但我还是建议你转成结构化的。
3.5 实战技巧:写解析规则前的准备工作
拿到一堆日志,别急着写正则。我推荐你按这个顺序来:
- 确定格式:看开头字符。<数字>开头是Syslog。CEF:开头是CEF。{开头是JSON。
- 确定时间格式:Syslog用本地时间且不带时区。JSON和CEF建议用UTC或ISO 8601。
- 确定分隔符:Syslog用空格和[]。CEF用管道和=。JSON用{和}。
- 确定必录字段:时间、源IP、事件类型、严重级别。查一下有没有缺的。
- 确定变体:同一个厂商的不同版本,字段顺序可能变。我之前就被Fortinet的版本差异坑过。
- 消息体里如果包含管道符|,CEF解析会直接崩掉。
- JSON里如果ip字段是字符串类型,你按整数解析,那条日志就丢了。
- Syslog的消息体如果跨行,很多系统只认第一行。
我建议你总在解析器外层套一个异常捕获。宁可丢一条格式错误的日志,也别让整个解析线程挂掉。
说到底,格式本身只是载体。你真正要理解的是——这条日志想告诉你什么?是攻击?是错误?还是正常行为?
格式清晰了,后面的分析才有基础。我见过太多人一上来就写复杂的正则,结果字段都对不上。花10分钟先看懂格式,比你折腾2小时正则划算得多。