日志生成与采集:应用日志生成、系统日志采集、容器日志采集、日志采集代理
日志采集这件事,说白了就是「把散落在各处的日志收拢到一起」。我刚开始做运维那会儿,服务器少,直接 ssh 上去 tail -f 就完事了。后来机器多了,容器化一上,好家伙,日志满天飞。今天我们就聊聊怎么把这些「碎片」捡起来。
应用日志生成:从源头规范起来
应用日志是日志体系的「原材料」。原材料质量不行,后面再怎么折腾也白搭。我个人习惯,在项目启动阶段就跟开发团队定好日志规范。
核心原则:结构化、可检索、有上下文。
什么叫结构化?就是别写这种日志:
2024-01-15 10:30:22 用户登录失败
而是写成 JSON 格式:
{
"timestamp": "2024-01-15T10:30:22.123Z",
"level": "ERROR",
"logger": "com.example.AuthService",
"message": "用户登录失败",
"userId": "u_10086",
"ip": "192.168.1.100",
"reason": "密码错误",
"duration": 45
}
我在项目中遇到过,开发同学图省事,日志全写在一行字符串里。结果排查问题时,grep 出来一堆无意义的信息,根本没法用。后来我强制要求所有新项目必须用 JSON 格式输出日志,配合日志采集工具解析,效率提升不止一倍。
另外,日志级别要合理使用。我见过有人把所有日志都打成 ERROR,结果真正出问题时,根本分不清轻重缓急。建议这样划分:
| 级别 | 使用场景 | 示例 |
|---|---|---|
| DEBUG | 开发调试,生产环境建议关闭 | SQL 语句、变量值 |
| INFO | 关键业务流程节点 | 用户注册成功、订单创建 |
| WARN | 潜在问题,不影响当前功能 | 接口响应超时、重试 |
| ERROR | 功能异常,需要人工介入 | 数据库连接失败、空指针 |
小技巧:生产环境建议只开 INFO 及以上级别。DEBUG 日志量太大,容易把磁盘撑爆。我见过有人线上开了 DEBUG,一天产生 200GB 日志,直接把磁盘打满了。
系统日志采集:别忽略操作系统本身
系统日志往往被忽视,但关键时刻能救命。你想想看,应用挂了,到底是应用自身的问题,还是操作系统层面的问题?这时候系统日志就是第一手证据。
Linux 系统日志主要分布在几个地方:
- /var/log/messages:通用系统日志,包含内核、服务等信息
- /var/log/secure:安全相关日志,SSH 登录、sudo 操作等
- /var/log/syslog:Debian/Ubuntu 系的主日志文件
- /var/log/dmesg:内核环缓冲区日志,硬件相关
采集系统日志,我推荐用 rsyslog 或 syslog-ng。它们原生支持将日志转发到远程服务器。配置起来也很简单:
# rsyslog 配置示例:将系统日志转发到日志中心
*.* @192.168.1.200:514
# 或者使用 TCP 协议,更可靠
*.* @@192.168.1.200:514
我曾经遇到过一个案例,某台服务器半夜突然重启,应用日志里没有任何异常。后来查了 /var/log/messages,发现是内核 panic 导致的。如果没有采集系统日志,这个根因可能永远找不到。
注意:系统日志默认使用 UDP 协议传输,可能会丢包。关键场景建议改用 TCP 或 RELP 协议。我曾经因为 UDP 丢包,漏掉了重要的安全告警日志,后来全部改成了 TCP。
容器日志采集:标准输出 vs 文件日志
容器化之后,日志采集的玩法变了。Docker 默认把容器的标准输出(stdout/stderr)收集到宿主机上,通过 docker logs 就能查看。但这种方式有个问题——日志轮转和持久化。
我建议遵循「12-Factor App」的原则:应用将日志输出到标准输出,由容器运行时负责采集。这样应用本身不需要关心日志写到哪里,运维层面统一处理。
Docker 的日志驱动配置:
# daemon.json 配置
{
"log-driver": "json-file",
"log-opts": {
"max-size": "10m",
"max-file": "3"
}
}
但现实往往没那么理想。很多老应用还是习惯写文件日志,比如 Tomcat 的 catalina.out、Nginx 的 access.log。这时候就需要在容器内挂载日志目录,或者使用 sidecar 模式采集。
Kubernetes 环境下,我常用的方案是:
- 标准输出日志:由 kubelet 采集到宿主机 /var/log/pods/ 目录下
- 文件日志:通过 emptyDir 卷挂载,由 sidecar 容器采集
- 持久化日志:使用 hostPath 或 PVC,直接写到宿主机磁盘
避坑指南:我曾经在 K8s 集群里,所有 Pod 的日志都写标准输出,结果一个 Pod 疯狂打日志,把宿主机磁盘写满了。后来加了日志限流和轮转策略,才稳住。记住,容器日志也要有「刹车」机制。
日志采集代理:Filebeat vs Logstash
日志采集代理,说白了就是「搬运工」。把日志从各个节点搬到中央存储。市面上主流的有 Filebeat 和 Logstash,它们各有千秋。
| 特性 | Filebeat | Logstash |
|---|---|---|
| 资源占用 | 极低(10MB 内存左右) | 较高(默认 1GB 内存) |
| 处理能力 | 轻量采集,简单过滤 | 强大过滤、转换、丰富 |
| 适用场景 | 边缘节点、容器环境 | 中心节点、复杂处理 |
| 插件生态 | 较少 | 丰富(200+ 插件) |
我个人习惯的架构是:Filebeat 负责采集,Logstash 负责处理。Filebeat 部署在每台机器上,只做一件事——读日志、发日志。Logstash 集中部署,做解析、过滤、格式化。
Filebeat 配置示例:
# filebeat.yml
filebeat.inputs:
- type: log
enabled: true
paths:
- /var/log/app/*.log
fields:
app: myapp
env: production
output.logstash:
hosts: ["192.168.1.200:5044"]
Logstash 配置示例:
# logstash.conf
input {
beats {
port => 5044
}
}
filter {
if [fields][app] == "myapp" {
json {
source => "message"
}
date {
match => ["timestamp", "ISO8601"]
}
}
}
output {
elasticsearch {
hosts => ["http://192.168.1.100:9200"]
index => "logs-%{+YYYY.MM.dd}"
}
}
关键点:Filebeat 有「背压」机制。当 Logstash 处理不过来时,Filebeat 会自动降速,不会把日志丢掉。这个特性在生产环境非常重要,我遇到过 Logstash 挂了,Filebeat 默默缓存日志,等恢复后自动补发,数据一条没丢。
嗯,这里要注意一点:Filebeat 的缓存是写磁盘的,默认路径是 /var/lib/filebeat/registry。如果磁盘空间不够,缓存可能会写满。我建议给这个目录单独挂载一个分区,或者限制缓存大小。
最后总结一下我的经验:
- 应用日志:强制 JSON 结构化,规范级别使用
- 系统日志:用 rsyslog 转发,别漏掉
- 容器日志:优先标准输出,文件日志用 sidecar
- 采集代理:Filebeat 采集 + Logstash 处理,黄金搭档
日志采集做好了,后面的分析、告警、排障才有基础。说白了,这就是地基活,看着不起眼,但决定了整栋楼能盖多高。