Metrics 告诉我们“系统发生了什么变化”,而 Logs 更擅长告诉我们“具体发生了什么”。
日志收集的核心并不是简单地把日志文件复制到另一台机器,而是建立一条从产生、采集、解析、传输、存储到查询分析的完整链路。
一、日志与日志系统
1.1 什么是日志
日志(Log)是系统运行过程中产生的事件记录。
例如:
2026-09-14 10:32:11 INFO Application started2026-09-14 10:32:15 INFO Request GET /api/users2026-09-14 10:32:18 ERROR Database connection refused一条日志通常包含:
时间级别来源消息上下文例如:
2026-09-14 10:32:18 │ ├── 时间 ├── ERROR ├── backend └── Database connection refused日志和 Metrics 的区别可以简单理解为:
Metrics→ 数值化状态
Logs→ 具体事件记录例如:
CPU = 95%属于:
Metric而:
OutOfMemoryError: Java heap space属于:
Log1.2 为什么需要日志
当监控发现:
HTTP 5xx ↑Metric 可以告诉我们:
错误率升高了但还需要知道:
什么请求失败?为什么失败?哪个服务失败?什么时候开始?异常信息是什么?这时就需要日志。
因此:
Metrics │ ▼发现异常 │ ▼Logs │ ▼分析原因这也是实际运维中:
监控+日志经常配合使用的原因。
二、日志的来源
一个 Linux 系统中,日志可能来自多个位置。
Logs │ ┌──────────────────┼──────────────────┐ │ │ │ ▼ ▼ ▼ System Application Container │ │ │ journald log file stdout syslog stdout stderr kernel stderr2.1 系统日志
Linux 本身会产生:
内核事件服务状态认证信息启动信息系统错误现代 Linux 系统中常见来源之一是:
systemd-journald查看:
journalctl例如查看:
journalctl -u nginx只查看最近一小时:
journalctl --since "1 hour ago"查看本次启动:
journalctl -b2.2 文件日志
很多应用会直接写入:
/var/log/例如:
/var/log/├── nginx/├── mysql/├── redis/└── ...Nginx 常见:
access.logerror.log应用也可能:
/app/logs/application.log因此日志采集器需要能够:
读取文件持续跟踪文件识别新增内容2.3 标准输出与标准错误
现代容器化应用非常常见的日志方式是:
stdoutstderr例如:
Application ├── stdout └── stderr │ ▼Container RuntimeKubernetes 官方也建议容器化应用将日志写入标准输出和标准错误,这也是 Kubernetes 日志收集最常见的基础方式。
Docker 同样提供专门的 Logging Driver 来处理容器的日志输出。Docker Engine 默认使用 json-file logging driver,但也支持 local、journald、syslog、fluentd 等方式。
三、日志级别
应用通常会对日志进行分级。
常见级别:
TRACEDEBUGINFOWARNERRORFATAL不同框架名称可能略有不同。
可以粗略理解:
DEBUG→ 调试细节
INFO→ 正常运行信息
WARN→ 潜在问题
ERROR→ 请求或操作发生错误
FATAL→ 严重错误,可能导致程序退出例如:
INFOApplication started
WARNConnection pool is near capacity
ERRORDatabase connection refused3.1 为什么不能全部输出 DEBUG
开发环境可能需要大量 DEBUG 日志。
但生产环境如果:
所有请求+所有变量+所有 SQL+大量调试信息全部输出,就可能导致:
日志量暴增磁盘增长网络带宽增加存储成本增加查询困难因此生产环境通常会根据实际需要设置日志级别。
四、日志格式
4.1 非结构化日志
最简单的日志:
2026-09-14 10:31:20 ERROR Database connection refused人比较容易阅读,但程序解析起来比较麻烦。
4.2 结构化日志
更适合集中式日志系统:
{ "timestamp": "2026-09-14T10:31:20Z", "level": "ERROR", "service": "backend", "message": "Database connection refused"}结构化日志可以明确区分:
timestamplevelservicemessage后续查询时可以直接按照字段过滤:
level = ERRORservice = backend而不是通过字符串搜索整行文本。
4.3 为什么结构化日志重要
假设有:
100 万条日志如果都是:
纯文本字符串分析器通常需要先进行:
解析提取字段如果日志本身已经结构化:
JSON那么采集系统更容易建立:
字段索引过滤条件聚合这会直接影响后面的集中式日志分析。
五、日志轮转 Log Rotation
5.1 为什么需要日志轮转
假设:
application.log每天增长:
2 GB一个月之后可能达到:
60 GB如果一直写同一个文件:
磁盘最终会被写满因此需要:
Log Rotation典型过程:
application.log │ ▼application.log.1 │ ▼application.log.2 │ ▼application.log.3.gz旧日志可以:
压缩保留删除5.2 logrotate
Linux 中常见工具:
logrotate可以根据:
时间文件大小轮转日志,并支持压缩、保留数量等操作。
例如配置:
/var/log/myapp/*.log { daily rotate 7 compress missingok notifempty}含义大致是:
每天轮转保留 7 份压缩旧日志日志不存在不报错空日志不轮转5.3 日志轮转的一个常见坑
应用打开了:
application.log执行轮转后:
application.log→ application.log.1如果应用仍然持有原文件描述符,就可能出现:
文件名已经变了但进程仍然写旧文件因此某些应用轮转后需要:
reloadreopenHUP等机制重新打开日志文件。
所以:
Log Rotation≠只改文件名还要考虑:
应用如何重新打开日志六、日志收集
6.1 什么是日志采集
日志采集就是:
Log Source │ ▼Collector / Agent │ ▼Backend例如:
/var/log/nginx/access.log │ ▼ Log Agent │ ▼ Central Log Server日志采集器通常需要完成:
读取解析过滤添加字段缓存批量发送6.2 Agent
Agent 通常运行在:
被监控服务器上。
例如:
Server A └── Log Agent
Server B └── Log Agent
Server C └── Log Agent统一发送:
┌── Server A ── Agent ──┐ ├── Server B ── Agent ──┤ └── Server C ── Agent ──┘ │ ▼ Central Log Backend这种方式非常适合大量服务器。
6.3 为什么通常使用 Agent
如果没有 Agent:
Central Server │ ├── SSH Server A ├── SSH Server B ├── SSH Server C └── ...中心服务器主动去每台机器拉日志,管理会比较复杂。
使用 Agent:
Server │ └── Agent │ └──► Central Backend日志产生在哪里,就在那里进行采集。
七、日志采集器应该做什么
一个成熟的日志采集器通常不仅是:
cat log而是完整的数据处理过程:
Log File │ ▼ Tail │ ▼ Parse │ ▼ Transform │ ▼ Enrich │ ▼ Buffer │ ▼ Send7.1 Tail
持续读取新增日志。
例如:
application.log应用继续写:
Line 100Line 101Line 102...Agent 只读取新增内容,而不是每次都重新读取整个文件。
7.2 Position / Offset
采集器需要知道:
“我已经读到哪里了?”例如:
application.log │ ├── 已读取到 Line 1000 │ └── 新增 Line 1001+因此通常需要保存:
文件位置offsetinode / file identity这样 Agent 重启后才能尽量从正确位置继续。
OpenTelemetry 的 filelog receiver 等日志采集能力同样包含文件跟踪、日志轮转识别和 checkpoint 等机制。
7.3 Parse
例如原始日志:
127.0.0.1 - - [14/Sep/2026:10:30:12 +0800] "GET /api HTTP/1.1" 200 1234采集器可以解析成:
client_ip = 127.0.0.1method = GETpath = /apistatus = 200bytes = 12347.4 Enrich
采集器还可以增加额外字段:
hostserviceenvironmentcontainerpodnamespace例如:
{ "service": "backend", "environment": "production", "host": "node-01", "level": "ERROR", "message": "Database connection refused"}这样后面的集中式系统就可以方便地进行过滤和聚合。
八、集中式日志
8.1 为什么需要集中式日志
假设有:
100 台服务器每台都有:
/var/log/app.log用户访问出现异常时,如果要求运维:
SSH 到 Server 1SSH 到 Server 2SSH 到 Server 3...逐台搜索:
ERROR效率会非常低。
集中式日志就是:
Server 1 ─┐Server 2 ─┤Server 3 ─┤Server 4 ─┤ ▼ Central Backend │ ▼ Search于是可以统一查询:
service = backendlevel = ERRORtime > 10:308.2 集中式日志的核心能力
一个日志平台通常需要:
采集传输存储索引查询过滤聚合可视化权限保留策略因此:
日志系统≠日志文件服务器它实际上是一整套数据处理系统。
九、集中式日志的典型架构
最简单:
┌── Agent ── Server A │Log Sources ────┼── Agent ── Server B │ └── Agent ── Server C │ ▼ Central Backend │ ▼ Query更完整:
Logs │ ▼Collection │ ▼Parsing │ ▼Buffer / Queue │ ▼Storage │ ▼Index │ ▼Search │ ▼Visualization中间还可以加入:
FilteringSamplingEnrichmentRouting十、日志传输
日志从 Agent 到中心系统,通常需要通过网络发送。
例如:
Agent │ │ TCP / HTTP / gRPC / Syslog ▼Log Backend常见问题:
网络断开连接超时服务不可用认证失败数据发送过快因此日志系统也会受到网络质量影响。
10.1 Buffer
假设:
日志产生速度>日志发送速度那么:
日志会积压例如:
Producer │ │ 10000 logs/s ▼Agent │ │ 5000 logs/s ▼Backend这时候需要考虑:
Memory BufferDisk BufferQueueBackpressure否则后端短暂故障可能直接造成日志丢失。
十一、日志可靠性
11.1 至少要回答几个问题
一个日志系统不能只问:
“能查到日志吗?”还应该考虑:
日志会不会丢?日志会不会重复?日志会延迟多久?后端挂了怎么办?磁盘满了怎么办?日志保留多久?11.2 At-least-once 与重复日志
日志采集中,经常需要在:
不丢日志与:
不重复之间做权衡。
例如:
发送成功但 Agent 在收到确认前突然崩溃。
重启后可能重新发送之前的数据:
Log ALog A于是出现:
Duplicate因此集中式日志系统经常需要依赖:
offsetackcheckpointid等机制提高可靠性。
对于日志而言:
至少一次通常比:
完全不重复更容易保证。
十二、Docker 日志
容器环境通常会把应用输出写到标准输出和标准错误:
stdoutstderr这里进一步从日志系统角度理解。
Container │ ├── stdout └── stderr │ ▼ Docker Logging Driver │ ▼ StorageDocker 当前默认 logging driver 为:
json-file同时还提供:
localsyslogjournaldgelffluentd...等驱动。
查看:
docker info也可以直接:
docker info --format '{{.LoggingDriver}}'查看当前默认 Logging Driver。
12.1 Docker 日志磁盘问题
容器日志如果持续增长:
Container │ ▼Logs │ ▼Host Disk │ ▼100%最终可能导致:
Docker 异常数据库异常应用写文件失败整个服务器磁盘不足因此:
容器日志+日志轮转+日志保留策略必须一起考虑。
Docker 官方也特别提示日志文件可能造成宿主机磁盘耗尽,并提供 local logging driver 等机制减少这类风险。
十三、Kubernetes 日志
Kubernetes 环境的日志结构比单机 Docker 更复杂。
Pod │ ├── Container A └── Container B │ ▼ stdout stderr │ ▼ Node-level logging │ ▼ Central LoggingKubernetes 官方将:
Node-level logging agent作为常见的集群级日志方案,并推荐在每个 Node 上运行一个日志 Agent;常见实现方式是使用 DaemonSet,使每个节点运行一个 Agent。
13.1 为什么不能只依赖 Pod 本地日志
Pod 可能:
被删除被重新调度Node 故障Container 重启如果日志只保存在:
Pod / Node 本地就可能随着运行环境消失。
因此集群日志应该尽量做到:
Pod Logs │ ▼Node Agent │ ▼Central Backend使日志生命周期独立于:
PodContainerNodeKubernetes 官方也明确指出,集群级日志应该拥有独立于 Node、Pod 和 Container 生命周期的存储与管理方式。
十四、Node Agent、Sidecar 与直接上报
Kubernetes 集群级日志常见有三种基本思路。
14.1 Node-level Agent
Node │ ├── Pod A ├── Pod B ├── Pod C │ └── Log Agent │ ▼ Backend优点:
每个 Node 一个 Agent应用基本无需修改这也是非常常见的方案。
14.2 Sidecar
Pod├── Application└── Log Sidecar │ ▼ Backend适合:
应用只能输出文件不同日志需要特殊处理但代价是:
每个 Pod 增加额外容器CPU / Memory 增加配置复杂14.3 应用直接发送
Application │ ▼Log Backend应用自己负责:
发送重试认证这样可以减少中间层,但会让应用与日志基础设施耦合。
因此:
Node Agent→ 通用性强
Sidecar→ 针对单个工作负载定制
Direct→ 应用主动管理日志输出十五、日志解析与多行日志
15.1 为什么多行日志是问题
很多异常不是一行:
ERROR Application failed而是一整个 Stack Trace:
ERROR Application failedjava.lang.RuntimeException: ... at com.example.App.run(App.java:100) at com.example.Main.main(Main.java:20)...如果采集器简单按:
一行 = 一条日志最终可能变成:
Log 1 → ERROR Application failedLog 2 → java.lang.RuntimeExceptionLog 3 → at com.example.App.runLog 4 → at com.example.Main.main原本的一次异常就被拆散了。
因此日志采集器需要支持:
Multiline Parsing把多行内容重新组合:
Stack Trace │ ▼Single Log Record15.2 多行解析要谨慎
如果正则配置错误:
正常日志可能被错误合并成:
一条超长日志所以多行规则需要根据具体应用日志格式设计。
十六、时间戳与时区
日志分析中非常重要的一项信息:
Timestamp例如:
2026-09-14 10:30:00如果不同服务器:
Server A → UTCServer B → UTC+8Server C → UTC+9集中后可能导致:
事件顺序混乱因此日志系统应该明确:
时间格式时区时间同步生产系统通常需要保证服务器时钟同步,否则:
Log+Metric+Trace之间可能难以正确关联。
十七、日志与敏感信息
日志里非常容易出现:
密码TokenCookieSession ID身份证号码手机号邮箱数据库连接信息这是一类很严重的问题:
应用正常运行 ↓日志记录敏感信息 ↓集中式日志平台 ↓更多人可以查询于是敏感信息的暴露范围反而扩大。
因此日志系统应该遵循:
不要记录不必要的敏感信息并同时考虑:
脱敏权限控制加密传输访问审计保留期限例如:
password=123456不应该直接写进生产日志。
更合理:
password=******或者根本不记录。
十八、日志保留策略
日志并不是:
保存越久越好因为日志量可能非常大。
例如:
每天 20 GB一年就是:
7 TB+因此需要制定:
Retention Policy例如:
热数据→ 7 天
温数据→ 30 天
归档→ 180 天具体策略取决于:
业务需求合规要求成本排障周期审计要求同时需要明确:
删除压缩归档之间的区别。
十九、日志系统的性能问题
日志系统本身也可能成为性能瓶颈。
主要观察:
日志产生速率采集速率传输速率写入速率查询速率存储容量例如:
Application │ │ 50 MB/s ▼Agent │ │ 40 MB/s ▼Backend那么:
10 MB/s会持续积压。
进一步:
Buffer→ 越来越大最后可能:
Agent 磁盘写满因此日志系统也需要监控:
Queue LengthDropped LogsSend ErrorsBackend LatencyDisk Usage这就是:
“监控日志系统本身”二十、日志故障排查
日志系统出现问题时,可以按照:
Source ↓Agent ↓Network ↓Backend ↓Query逐层排查。
20.1 Agent 没有采集
例如:
application.log有新日志但中心平台没有。
首先:
文件路径对吗?然后:
Agent 是否运行?再:
Agent 是否读取到新增内容?然后:
offset 是否正确?20.2 日志采集后没有发送
查看:
Agent Logs检查:
连接认证TLS后端地址队列20.3 网络问题
检查:
ss -ntp以及:
nc -vz <backend> <port>如果使用 HTTP,可以:
curl -v http://<backend>:<port>20.4 后端收到了,但查不到
这时继续检查:
索引时间范围字段解析写入状态查询条件一个非常常见的问题:
日志实际上已经写入但 Dashboard 查询的:
时间范围不正确。
二十一、一个完整的集中式日志案例
假设有三台 Web Server:
Server 1Nginx
Server 2Nginx
Server 3Nginx每台都有:
access.logerror.log架构:
Server 1 ├── access.log ├── error.log └── Agent │ │Server 2│ ├── access.log ├── error.log └── Agent │ │Server 3│ ├── access.log ├── error.log └── Agent │ └──────────────┐ ▼ Central Log Backend │ ▼ Search │ ▼ Visualization最终可以统一查询:
service = nginxstatus >= 500time = last 1 hour得到:
Server 1 → 502Server 2 → 500Server 3 → 502然后再进一步根据:
upstreamrequest_uriclient_ipresponse_time定位问题。
二十二、日志与 Metrics 的结合
假设 Prometheus 发现:
HTTP 500 ↑Grafana 显示:
5xx │ │ /\ │ / \ │______/ \____这个时候可以根据时间:
10:30去查询日志:
10:30 ERROR Database connection refused于是:
Metric→ 告诉你异常从什么时候开始
Log→ 告诉你具体发生了什么这就是可观测性中非常核心的协同方式。
二十三、日志与 Trace 的关系
随着系统变成微服务:
Client │ ▼API Gateway │ ▼Service A │ ▼Service B │ ▼Database一次用户请求可能经过:
ABCD此时只搜索:
ERROR可能找到数千条日志。
如果日志中包含:
trace_idspan_id就可以:
Trace ID = abc123查询与这一次请求有关的日志:
Service AService BService C于是:
Metrics │ ▼异常发生
Logs │ ▼具体错误
Traces │ ▼请求经过哪些服务OpenTelemetry 当前的日志设计也非常重视与其他遥测信号的关联,并支持把 Trace ID、Span ID 等上下文关联到日志中。
不过:
Trace、OpenTelemetry 和完整的 Metrics / Logs / Traces 体系属于进阶主题。
二十四、常见日志采集工具
日志采集领域存在大量工具。
典型可以分成:
轻量 Agent │ ├── Fluent Bit ├── Vector └── Filebeat以及:
Telemetry Collector │ └── OpenTelemetry Collector例如 Fluent Bit 可以作为轻量级日志采集器,负责:
Input→ Filter→ Output而 OpenTelemetry Collector 则进一步提供:
Receiver→ Processor→ Exporter这样的通用遥测处理管道,并支持日志数据。
OpenTelemetry 官方日志规范明确支持:
文件日志日志轮转文件位置 checkpointSyslog解析处理导出并强调与现有日志系统和日志库进行兼容。
二十五、日志系统的整体模型
到这里可以把日志系统串起来:
Application │ ┌────────┴────────┐ ▼ ▼ Log File stdout │ │ └────────┬────────┘ ▼ Agent │ ┌─────────┼─────────┐ ▼ ▼ ▼ Parse Filter Enrich │ │ │ └─────────┼─────────┘ ▼ Buffer │ ▼ Network │ ▼ Central Backend │ ┌─────────┼─────────┐ ▼ ▼ ▼ Storage Index Query │ ▼ Visualization因此集中式日志系统真正解决的是:
统一采集统一存储统一检索统一分析二十六、日志系统与 ELK / Elastic Stack
后续文章会进入:
ELK / Elastic Stack最经典的组合:
Log │ ▼Logstash │ ▼Elasticsearch │ ▼Kibana可以暂时这样理解:
Logstash→ 日志采集 / 处理 / 转发
Elasticsearch→ 日志存储 / 索引 / 查询
Kibana→ 日志可视化 / 分析不过现代 Elastic Stack 的实际架构可以加入:
BeatsElastic AgentLogstashElasticsearchKibana等不同组件。
因此这一篇只建立:
日志为什么要集中化下一篇再重点研究:
ElasticsearchLogstashKibana的具体实现。
二十七、日志收集与分析的运维思维
从运维角度,一条日志真正的生命周期是:
产生 ↓采集 ↓解析 ↓传输 ↓存储 ↓索引 ↓查询 ↓分析 ↓保留 / 删除任何一环出现问题,都可能导致:
日志丢失日志延迟查询不到磁盘爆满成本增加所以排查日志问题时,不应该只检查:
“日志平台有没有这条日志”而应该从源头一路追:
Application ↓Log Source ↓Agent ↓Buffer ↓Network ↓Backend ↓Index ↓Query二十八、总结
日志系统可以先记住下面这条主线:
Log Source │ ▼Collection Agent │ ├── Tail ├── Parse ├── Filter ├── Enrich └── Buffer │ ▼Central Backend │ ├── Storage ├── Index └── Query │ ▼ VisualizationLinux 环境中:
systemd-journaldlogrotate应用日志文件stdout / stderr解决的是:
日志产生与本地管理而集中式日志进一步解决:
多服务器多容器多服务统一采集统一搜索最终可以把三类可观测数据暂时区分为:
Metrics→ 数值变化→ Prometheus
Logs→ 具体事件→ 日志系统 / ELK
Traces→ 请求调用链→ OpenTelemetry 等下一阶段再把它们逐渐关联起来:
Metrics │ ▼发现异常 │ ▼Logs │ ▼定位错误 │ ▼Traces │ ▼定位调用链上的具体环节日志收集不是“把文件搬到另一台机器”,而是建立一条可持续、可检索、可分析的数据管道。对于运维而言,真正重要的是知道日志从哪里产生、经过哪些环节、在哪里可能丢失,以及如何最终利用日志定位故障。