日志采集 的十种打开方式:工程实践笔记
1. stdout 重定向:最简单也最容易翻车
我一开始以为日志采集嘛,最朴素:程序往 stdout/stderr 打,然后重定向到文件。
./myapp --debug > /var/log/myapp/app.log 2>&1 &
结果生产上跑了两天,磁盘就报警了。问题出在文件描述符一直指着同一个 inode,logrotate 如果配置得不好,要么旧日志删不掉,要么进程还往已经 rotate 掉的文件里写。这个方式适合本地调试、临时验证,真到长期运行,最好交给 systemd、docker、journald 或者采集器。
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。真正复杂场景里,Parser 和 Tag 得提前规划好,不然输出到后端以后,谁是谁都不清楚了。
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_id、request_id、trace_id 都塞进标签,Loki 会疯。标签只放少量、稳定、能帮你快速定位日志流的字段,比如 app、env、region、level,剩下的内容留在日志正文里用查询解决。
评论
还没有评论。
发表评论
提交后评论将经过自动审核,审核通过后公开展示。