日志类型与格式:从零看懂每一条日志

很多刚接触日志采集的同事会问我:
「日志不就是程序输出的文本吗?有什么好区分的?」

说实话,我刚开始也这么想。直到有一次线上故障,我盯着几万条混杂的日志,愣是找不到一条关键错误。那时候我才意识到——不懂日志类型和格式,采集和排障根本无从下手。

这一章,我们就来把这些基础打扎实。

2.1 三种核心日志类型

生产环境中,日志按来源可以分为三大类。我个人习惯这样区分:

系统日志

操作系统产生的日志。包括内核日志、系统服务日志、硬件事件等。
在 Linux 上,你会在 /var/log/messages 或 /var/log/syslog 里看到它们。

我记得有一次排查服务器频繁重启,就是靠系统日志里的 Out of memory 关键字锁定的问题。
说白了,系统日志是服务器的「体检报告」——硬件故障、OOM、磁盘 I/O 错误都在这里。

应用日志

应用程序自己输出的日志。这是采集量最大、最复杂的一类。
从 Java 的 log4j、Python 的 logging,到 Nginx 的 access log,都属于应用日志。

避坑指南:我曾经见过一个团队把所有业务日志都打到同一个文件,结果排查一个订单问题时,不得不从 50GB 的文件里手动 grep。嗯,后来他们上了日志采集系统,但文件级别的切分和索引还是让我头疼了很久。

应用日志的关键在于:格式统一、内容规范。你看,同样的用户登录失败,有人输出 login failed for user xxx,有人输出 UserLoginException: invalid password。解析起来完全是两种工作量。

安全日志

跟系统安全相关的日志。登录记录、权限变更、防火墙规则匹配等。

在 /var/log/secure(RHEL/CentOS)或 /var/log/auth.log(Debian/Ubuntu)里能找到。
安全日志的特点是:不可篡改要求高、审计链路长。很多合规场景(比如 PCI-DSS、等保)都要求安全日志至少保留 6 个月以上。

我的个人习惯:系统日志保留 90 天,应用日志保留 30 天,安全日志至少保留 180 天。磁盘不够?压缩后归档到冷存储,但绝不能直接删。

2.2 日志的两种结构

你想想看,同样是记录一个 HTTP 请求:

// 非结构化
2025-01-15 10:00:00 GET /api/user 200 120ms

// 结构化
{"timestamp":"2025-01-15T10:00:00Z","method":"GET","path":"/api/user","status":200,"duration_ms":120}

一目了然。

非结构化日志

没有任何固定格式。靠正则或关键词来提取信息。
优点是输出简单,开发成本低。
缺点是解析困难,而且一旦日志内容发生微小变化,正则就失效了。

我说的是——你在一个遗留的 Perl 系统里看到的那种日志。每行开头是时间,但格式不统一,有时空格多一个,有时少一个。解析这种日志,就是在跟「玄学」打交道。

结构化日志

用 JSON、Protobuf、Avro 等格式输出。
每个字段都预先定义好 key 和 value。解析器直接按 key 提取,基本没有歧义。

我建议:所有新项目从一开始就用结构化日志。别等到上了生产再来改造——那工作量,你真的不想知道。

对比维度 非结构化日志 结构化日志
解析复杂度 高需要正则/模式匹配 低直接按 key 取值
存储效率 中等文本压缩率高 较低有结构开销
查询条件 仅全文搜索 支持字段级过滤/聚合
开发成本 低直接 print 高需要定义 schema

2.3 常见日志格式

下面是我在生产环境中最常遇到的三种格式。每一种都有它的适用场景和坑。

JSON 格式

当前最主流的结构化日志格式。几乎所有现代日志采集系统(ELK、Loki、Datadog)都原生支持 JSON 解析。

{
  "@timestamp": "2025-01-15T10:00:00.123Z",
  "level": "ERROR",
  "logger": "com.example.UserService",
  "message": "用户登录失败",
  "user_id": "u10086",
  "ip": "192.168.1.1",
  "duration_ms": 2345
}

注意:JSON 日志千万不要跨行。每条日志单独一行,否则解析器会报错。我见过有人在 Java 代码里用 Logger.info(objectMapper.writeValueAsString(obj)),结果 obj 里有换行符,日志行就被拆成了两段——采集端直接丢弃了后半段。

我曾踩过的坑:JSON 日志中如果包含 \n、\t 等特殊字符,一定要 escape。否则 Fluentd 或 Logstash 解析时会抛异常,导致整条日志丢失。

Syslog 格式

老牌日志协议,从 BSD 时代就在用。采用 <优先级>日期 主机 进程[PID]: 消息 的结构。

<14>Jan 15 10:00:00 webserver01 nginx[12345]: 192.168.1.1 - - "GET /api/user HTTP/1.1" 200 1024

优先级是 facility * 8 + severity 算出来的。比如 14 表示 user-level messages (1) * 8 + info (6) = 14。

Syslog 的优点是标准化程度高,几乎所有的网络设备和 Linux 服务都支持。
缺点是字段少、结构松散。想记录自定义的业务字段?得自己拼到消息体里。

我建议:网络设备日志、防火墙日志这类场景用 Syslog。业务应用还是别用它了。

Apache/Nginx 访问日志格式

Web 服务器最常用的日志格式。可以自定义,但有几个标准模板:

  • Common Log Format (CLF):远程IP 身份 用户名 [时间] "请求" 状态码 响应大小
  • Combined Log Format:在 CLF 基础上增加了 Referer 和 User-Agent
192.168.1.1 - - [15/Jan/2025:10:00:00 +0800] "GET /api/user HTTP/1.1" 200 1024 "https://example.com" "Mozilla/5.0"

这类日志字段虽多,但解析起来相对简单。因为它格式固定,空格和引号作为分隔符。常用的日志采集器(Filebeat、Fluent Bit)都有现成的解析插件。

嗯,这里要注意:时间格式。Apache 用的是本地时间,不是 UTC。如果你有多地部署的服务器,采集后一定要统一转成 UTC。否则做跨集群的时间聚合时,你会发现自己陷入了时区地狱。

2.4 一张图理清日志类型与格式

日志类型与格式核心框架 三大日志类型 系统日志 应用日志 安全日志 典型场景: /var/log/messages Nginx access_log /var/log/secure 通用核心字段 时间戳 · 主机名 · 进程名/PID · 日志级别 · 消息体 日志结构 非结构化(纯文本) 结构化(JSON等) 我建议:新项目全部用结构化 遗留系统逐步迁移 常见具体格式 Syslog JSON Apache/Nginx

2.5 格式选择的一些实战建议

说了这么多理论,落到实际怎么选?我个人的经验是这样的:

  • 微服务/云原生应用:优先 JSON,配合结构化日志库(如 logrus、python-json-logger),输出直接进 ES 或 Loki,省去解析步骤。
  • 遗留系统/第三方软件:大部分输出非结构化日志。我会在采集端用 Logstash 或 Fluent Bit 的正则插件做现场解析。实在解析不了的,先用 grok 兜底,再逐步推动业务方改造。
  • 网络设备/防火墙:只能用 Syslog。我会在采集器前面加一个 rsyslog 转发层,把多设备的 Syslog 统一汇聚后,再做结构化转换。
  • Web 服务器:直接采默认格式。Filebeat 的 Nginx 模块可以直接解析,几乎零配置。

一句话总结:日志类型决定了「采什么」,日志结构决定了「怎么采」,格式决定了「采完怎么用」。这三者搞清楚了,你的日志采集系统就已经有了半个骨架。