日志生成与采集:应用日志生成、系统日志采集、容器日志采集、日志采集代理

日志采集这件事,说白了就是「把散落在各处的日志收拢到一起」。我刚开始做运维那会儿,服务器少,直接 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 处理,黄金搭档

日志采集做好了,后面的分析、告警、排障才有基础。说白了,这就是地基活,看着不起眼,但决定了整栋楼能盖多高。