小Cの已经记不起来的博客

日志采集 的十种打开方式:工程实践笔记

1. stdout 重定向:最简单也最容易翻车

我一开始以为日志采集嘛,最朴素:程序往 stdout/stderr 打,然后重定向到文件。

./myapp --debug > /var/log/myapp/app.log 2>&1 &

结果生产上跑了两天,磁盘就报警了。问题出在文件描述符一直指着同一个 inodelogrotate 如果配置得不好,要么旧日志删不掉,要么进程还往已经 rotate 掉的文件里写。这个方式适合本地调试、临时验证,真到长期运行,最好交给 systemddockerjournald 或者采集器。

2. systemd journal:服务日志交给系统管家

如果是自己管理的 Linux 服务,我会优先考虑 systemd 的 journal。

在 unit 里可以直接这样:

[Service]
ExecStart=/usr/bin/myapp --debug
StandardOutput=journal
StandardError=journal
Restart=always

然后查看:

journalctl -u myapp --since "1 hour ago" -f

这个方式很省心,不用自己处理文件轮转,至少 journald 会兜底。但坑也有:默认保存策略可能不够激进,磁盘还是会被日志吃掉。可以改 /etc/systemd/journald.conf,比如:

MaxUse=1G
MaxRetentionSec=7day

然后:

systemctl restart systemd-journald

不出问题的话,日志就会被限制在可接受范围里。

3. Docker json-file:容器日志的默认打开方式

Docker 默认就是 json-file,很多人直接 docker logs 一用就完事了。

docker run \
  --log-driver json-file \
  --log-opt max-size=20m \
  --log-opt max-file=5 \
  myapp:latest

这个方式适合单机容器,简单直接。但别裸用默认配置,默认日志容易无限膨胀。json-file 的坑在于:它只是把 stdout/stderr 落盘成 JSON,格式固定、体积不小,高吞吐场景下 IO 压力会比较明显。

如果你只是想看最后几百条日志,没问题;如果你想做集中采集、全文检索、告警分析,它最多算一个起点,不太适合当终点。

4. Kubernetes sidecar:多容器场景下老老实实分家

Kubernetes 里我最常碰到一类情况:应用不想把日志直接打到 stdout,非要写文件。比如老 Java 应用、Nginx、某些 Go 服务,写文件很稳,但改输出方式要折腾。

这时候 sidecar 就很实用。

volumes:
  - name: log-volume
    emptyDir: {}

containers:
  - name: app
    image: myapp:latest
    volumeMounts:
      - name: log-volume
        mountPath: /var/log/app

  - name: logshipper
    image: fluent-bit:latest
    volumeMounts:
      - name: log-volume
        mountPath: /var/log/app

sidecar 去 tail 这个目录,然后把日志发出去。这个方式隔离性不错,采集器和业务解耦,业务不用改太多。

但坑也别藏着:emptyDir 是节点本地存储,容器挂了可能还在,但 Pod 被驱逐、节点重启就可能丢。日志量一大,还会把 emptyDir 打爆。真要长期方案,我更倾向 DaemonSet,除非某个应用日志格式太特殊,确实需要 sidecar。

5. Filebeat:轻量采集,适合先把日志搬出来

Filebeat 是我比较喜欢拿来“先活下来”的方案。不追求多复杂,先把日志从机器里搬出去。

filebeat.yml 大概长这样:

filebeat.inputs:
  - type: filestream
    paths:
      - /var/log/myapp/*.log
    fields:
      app: myapp
      env: prod

output.console:
  pretty: true

确认能读到了,再把输出换成 Elasticsearch、Logstash、Kafka 或者 Loki。

output.loki:
  hosts:
    - http://loki:3100/loki/api/v1/push

这个方式适合应用日志、Nginx 日志、访问日志这些文本日志。坑主要有几个:文件权限不对读不到、.gz 轮转文件被重复采集、多行 Java 异常拆成很多条。多行这个最好早点处理,不然后面查询会很难受。

6. Fluent Bit:嵌入式采集器,适合容器日志多行

Fluent Bit 比 Filebeat 更轻一点,在容器环境里特别常见,尤其是 Kubernetes 的日志采集链路。

配置片段:

[INPUT]
    Name    tail
    Path    /var/log/myapp/*.log
    Tag     myapp.*
    Parser  json

[FILTER]
    Name   parser
    Match  myapp.*
    Parser json
    Key    log

[OUTPUT]
    Name  http
    Match myapp.*
    Host  loki.local
    Port  3100
    URI   /loki/api/v1/push

Fluent Bit 的好处是解析、过滤、输出链路比较完整,插件也多。尤其是多行日志处理,比单纯 tail 再正则拼接要体面。

不过配置语法一开始看着有点怪,像老式 ini 套了一堆 pipeline。真正复杂场景里,ParserTag 得提前规划好,不然输出到后端以后,谁是谁都不清楚了。

7. Vector:管道型选手,路由比想象中有用

Vector 我后来用下来,最大感受是:它很适合做日志“分流”。

不是所有日志都适合进同一个地方。调试日志可以进对象存储,错误日志可以进告警系统,审计日志可以进 Kafka,业务指标可以从结构化日志里抽出来。

配置类似这样:

[sources.app]
type = "file"
include = ["/var/log/myapp/*.log"]

[transforms.error_only]
type = "filter"
inputs = ["app"]
condition = '.level == "error"'

[sinks.loki]
type = "loki"
inputs = ["error_only"]
endpoint = "http://loki:3100/loki/api/v1/loki/push"

Vector 的坑在于它太像管道了,容易一开始就把路由、转换、重试、缓冲、编码全堆上。配置越灵活,越容易出现“看起来都能跑,实际线上没数据”的情况。建议先跑最小配置,再逐步加 transform。

8. rsyslog / syslog-ng:传统集中转发,别嫌老

很多人一看到 rsyslog 就觉得老掉牙,但我承认它在 Linux 主机日志集中收集里还是有价值。系统日志、安全日志、某些中间件日志,最后都可能走到 syslog 这条路。

rsyslog.conf 里监听 UDP:

module(load="imudp")
input(type="imudp" port="514")

然后集中转发:

*.* action(type="omfwd" target="logserver" port="514" protocol="tcp")

这个方式胜在通用,很多老系统都支持。坑也很传统:UDP 丢包没人告诉你,日志源没统一时间戳会查错顺序,权限、SELinux、防火墙经常一锅端。别用 UDP 做核心链路,至少用 TCP,或者中间加一层可靠传输。

9. OpenTelemetry Collector:日志、指标、trace 一起走

如果你的项目已经在接 OpenTelemetry,那日志也可以顺手走 OTel Collector。

receivers:
  filelog:
    include:
      - /var/log/myapp/*.log
    operators:
      - type: json_parser
        timestamp:
          parse_from: attributes.time
          layout: "%Y-%m-%dT%H:%M:%S.%fZ"

processors:
  batch: {}

exporters:
  otlphttp:
    endpoint: http://otel-backend:4318

这个方式的好处是语义统一,trace_id 和 log 能更容易关联。尤其是微服务排障时,从日志跳 trace,体验会很好。

但 OTel Collector 不是万能的。日志量特别大时,Collector 本身很容易变成瓶颈。建议至少配磁盘缓冲、背压和限流,不然业务日志还没查到,Collector 先把机器拖挂了。

10. Loki + Promtail:中小规模查日志的性价比打开方式

如果只是想看日志、查关键字、做基本告警,不是一上来就要全文索引,我会考虑 Loki + Promtail。

Promtail 配置:

server:
  http_listen_port: 9080

positions:
  filename: /tmp/positions.yaml

scrape_configs:
  - job_name: myapp
    static_configs:
      - targets:
          - localhost
        labels:
          job: myapp
          env: prod
          __path__: /var/log/myapp/*.log

Promtail 会把日志推到 Loki。这个链路轻,成本也低。Loki 的思路不是把所有日志都建重索引,而是通过标签定位,再在查询时过滤。

它最大的坑是标签基数。把 user_idrequest_idtrace_id 都塞进标签,Loki 会疯。标签只放少量、稳定、能帮你快速定位日志流的字段,比如 appenvregionlevel,剩下的内容留在日志正文里用查询解决。

评论

还没有评论。

发表评论

提交后评论将经过自动审核,审核通过后公开展示。

未在播放