☰
Linux日志管理实战:journald与logrotate配置及排障指南
2026/10/26 15:38:19 网站建设 项目流程

1. 日志管理这件事,为什么值得单独拎出来讲

刚入行那会儿,我对日志的理解就停留在“出问题了去/var/log底下翻一翻”这个层面。直到有一次线上服务半夜挂了,我登录机器,面对一个 8 个 G 的messages文件,tail卡了半天,grep跑得风扇狂转,最后靠less加shift+g硬翻,才在几万行里定位到一条磁盘写满的报错。那次之后我才真正意识到,日志管理不是“会看日志”这么简单,它是一套从产生、轮转、存储、检索到清理的完整工程习惯。你不管它,它就反过来搞你。

这篇内容我想聊的就是 Linux 环境下日志管理这套东西。它适合谁?我觉得三类人最该看:一是刚接手 Linux 服务器、还在用cat硬看日志的新手;二是负责几台到几十台机器、被磁盘告警折腾过的运维或后端;三是对系统可观测性有要求、想把日志这摊事理顺的开发者。核心关键词就一个——日志管理,围绕它展开的是日志的采集、轮转、集中检索和容量控制。我不会只告诉你journalctl怎么用,更想讲清楚为什么这么设计、参数怎么算、坑在哪里。

先把结论摆前面:Linux 日志管理有两条主线,一条是老牌的rsyslog加logrotate组合,另一条是systemd-journald这套二进制日志体系。两者不是替代关系,而是并存互补,很多发行版上它们同时跑着。你要做的是搞清楚每条线各自管什么、边界在哪,然后针对自己的场景做取舍。下面我按“整体设计思路—核心细节—实操落地—问题排查”这个顺序往下拆,中间穿插我自己踩过的坑和实测有效的做法。

2. 整体设计与思路拆解:两套体系为什么并存

2.1 rsyslog 与 journald 的分工逻辑

很多人第一次接触会困惑:既然有了systemd-journald,为什么还要留着rsyslog?反过来也一样。要理解这个,得先看两者各自的定位。journald是 systemd 生态的一部分,它直接从内核、服务进程的标准输出、syslog socket 等多个来源抓日志,存成带结构化字段的二进制文件,查询快、字段全,还能按服务单元、PID、优先级过滤。而rsyslog是一个成熟的 syslog 守护进程,强项在于规则路由和远程转发——它能按 facility、severity、来源等条件把日志分发到不同文件、不同目录,甚至转发到远端服务器。

所以常见的架构是这样:journald负责本地采集和短期存储,rsyslog从 journald 或 syslog socket 读取,按规则落盘到/var/log/下的文本文件,同时承担向集中日志平台转发的职责。这个分工的好处是各取所长——journald 的结构化查询能力用来做快速排障,rsyslog 的文本落盘和转发能力用来做长期归档和集中管理。你要是把两者对立起来,非要二选一,反而会丢掉其中一方的优势。

我见过有团队为了“简化”,直接把 rsyslog 停掉,只留 journald。结果呢,日志全在二进制文件里,想拿awk、sed做文本处理就得先journalctl -o json转一道,脚本复杂度上去了;更要命的是远程转发没配好,机器一挂日志跟着丢。这就是没想清楚边界。我的建议是:本地排障靠 journald,长期归档和转发靠 rsyslog,两条线都留着,各干各的活。

2.2 日志轮转为什么必须做,不做会怎样

日志轮转(log rotation)是日志管理里最容易被忽视、又最容易出事的一环。原理很简单:日志文件不能无限增长,得按时间或大小切分,旧的压缩、超期的删除。听起来是小事,但不做的后果很直接——磁盘写满,服务写日志失败,进而可能引发进程异常甚至整机不可用。我前面说的那次半夜事故,根子就是轮转策略配得太松,单个文件上限设了 500M,但保留份数给了 20 份,算下来光一个messages就能吃掉 10G。

这里有个简单的容量估算,值得每个人动手算一遍。假设某服务每天产生日志约 200MB,你希望保留 14 天,压缩比按常见的 10:1 估算,那么磁盘占用大约是200MB × 14 / 10 ≈ 280MB(压缩后),加上当天的未压缩文件 200MB,总共不到 500MB。这个量级对大多数机器都很轻松。但如果你把保留天数设成 90 天、又不压缩,那就是200MB × 90 = 18GB,小磁盘的机器直接告急。所以轮转策略的核心不是“配个数字”,而是先估算日增量,再倒推保留策略,让磁盘占用落在可控区间。

2.3 集中式日志与本地日志的取舍

单机日志管理做到位,只能解决“这台机器不出事”。一旦机器数量上去,登录每台去grep就不现实了。这时候要考虑集中式日志:把所有机器的日志汇总到一个地方,统一检索、统一告警。常见做法是用 rsyslog 的远程转发功能,把日志发到一台日志服务器,或者接入更完整的日志平台。

但集中式不是无脑上。它带来三个新问题:网络带宽、日志丢失风险、以及日志服务器本身的容量规划。我的经验是分阶段来:机器少于十台,先把本地轮转和 journald 查询做扎实,够用;超过十台或者有合规留存要求,再上集中式。上集中式时,转发链路一定要配磁盘缓冲队列,防止网络抖动时日志直接丢——rsyslog 的action.queue相关参数就是干这个的,后面实操部分我会给具体配置。这个取舍背后其实是个成本问题:本地管理成本低但检索效率差,集中管理检索强但要多维护一套东西,按规模选就行,别一上来就搞大而全。

3. 核心细节解析与实操要点

3.1 journald 的关键配置项怎么调

journald的配置文件在/etc/systemd/journald.conf,里面参数不少,但真正影响日常的就这么几个。第一个是Storage,可选volatile(只存内存,重启即失)、persistent(存/var/log/journal,重启保留)、auto(有目录就持久化,没有就内存)。生产环境我一般显式设成persistent,避免重启后关键日志丢失。第二个是SystemMaxUse,控制 journal 占用的磁盘上限,这个必须设,否则默认可能吃掉磁盘的 10%。第三个是SystemMaxFileSize,单个 journal 文件的大小上限,影响轮转粒度。

调这几个参数有个顺序:先定SystemMaxUse,再定SystemMaxFileSize,最后看MaxRetentionSec(按时间保留)。举个例子,一台 40G 磁盘的机器,我给SystemMaxUse=2G,SystemMaxFileSize=100M,这样最多 20 个文件轮着来,检索时不会因为单文件太大而变慢。改完配置记得systemctl restart systemd-journald,然后journalctl --disk-usage确认实际占用。这里有个细节:SystemMaxUse是软上限,journald 在清理时可能略微超出,别指望它精确到字节。

注意:Storage=persistent需要/var/log/journal目录存在,有些精简系统默认没有这个目录,得手动mkdir -p /var/log/journal再重启服务,否则配置不生效。

3.2 logrotate 配置文件的四段式结构

logrotate的配置我习惯按四段来理解和写:匹配哪些文件、怎么轮转、轮转后做什么、保留多少。一个典型的配置长这样:

/var/log/myapp/*.log { daily rotate 14 size 100M compress delaycompress missingok notifempty copytruncate create 0640 appuser appgroup }

逐行解释一下背后的逻辑。daily和size 100M同时写时,是“满足任一条件就轮转”,即每天一次或文件到 100M 就切,这个组合适合日志量波动大的服务。rotate 14是保留 14 份历史。compress压缩旧文件,delaycompress让最近一份先不压,方便还在写的进程继续追加——这个细节很多人漏掉,导致压缩时和写入进程抢文件。copytruncate是给那些不支持重新打开日志文件的程序用的:先复制再清空原文件,代价是复制瞬间可能丢几行日志。如果程序支持SIGHUP重开文件,更推荐用postrotate发信号,而不是copytruncate。

create 0640 appuser appgroup这行也别省,它保证轮转后新文件的权限和属主正确,否则可能出现新日志文件属主变成 root、应用写不进去的情况。我踩过这个坑:某次轮转后应用突然报“权限拒绝”,查了半天才发现是create没配,新文件默认 root 属主。

3.3 日志级别与 facility 的对应关系

syslog 体系里有两个维度:facility(谁产生的)和severity(多严重)。facility 常见的有auth(认证相关)、cron(定时任务)、daemon(后台服务)、kern(内核)、local0到local7(留给自定义应用)。severity 从高到低是emerg、alert、crit、err、warning、notice、info、debug。rsyslog 的规则就是靠这两个维度做路由的。

理解这个对应关系,写规则时就不容易乱。比如你想把认证失败单独拎出来,规则可以写auth.warning /var/log/auth-warnings.log,意思是 facility 为 auth、severity 为 warning 及以上的都进这个文件。注意 severity 是“及以上”,写warning会同时捕获err、crit这些更严重的。自定义应用建议用local0到local7,别去挤daemon,否则和系统服务混在一起,排查时很难区分。这个约定俗成的分配方式,团队内部最好统一,写进文档,不然换个人接手就乱套。

3.4 日志文件权限与敏感信息处理

日志里经常混着敏感信息——认证 token、用户标识、请求参数。默认的/var/log权限是755,日志文件多为640,属主root、属组adm或syslog。这个权限意味着同组用户能读,其他用户不能。如果你的应用日志里有敏感字段,光靠文件权限不够,得在应用层做脱敏,或者在 rsyslog 落盘前用模板过滤。

我一般会做两件事:一是给应用日志单独建目录,权限设750,属组只放需要看日志的人;二是定期审计日志内容,用grep扫一遍常见敏感模式(比如长串的十六进制、password=这类关键字),发现就回头改应用代码。这事听起来麻烦,但真出过一次日志泄露之后,你会觉得这点功夫花得值。权限这块没有一劳永逸的方案,只能靠约定加定期检查。

4. 实操过程与核心环节实现

4.1 从零配置一套 journald 持久化存储

假设你接手一台新机器,要先把 journald 的持久化配好。第一步,确认目录:ls -ld /var/log/journal,不存在就mkdir -p /var/log/journal。第二步,编辑/etc/systemd/journald.conf,把下面几项取消注释并改成目标值:

[Journal] Storage=persistent SystemMaxUse=2G SystemMaxFileSize=100M MaxRetentionSec=30day

第三步,重启服务并验证:systemctl restart systemd-journald,然后journalctl --disk-usage看当前占用,journalctl --verify检查文件完整性。第四步,做一次实际查询验证字段过滤是否好用,比如journalctl -u sshd --since "1 hour ago" -p warning,这条命令按服务单元、时间、优先级三个条件过滤,是日常排障最常用的组合。

这里有个参数选择的计算过程值得说清楚。SystemMaxUse=2G是怎么来的?我按“保留 30 天、日均日志 50MB、压缩后约 5MB/天”估算,30 天约 150MB,留足余量给突发流量,2G 足够覆盖。如果你的机器日志量大,比如日均 500MB,那 2G 只够存三四天,就得往上调,或者缩短MaxRetentionSec。先估日增量,再定上限,这个顺序不能反。

4.2 手写一份生产可用的 logrotate 配置

接着给一个自定义应用配轮转。假设应用日志在/var/log/myapp/下,程序支持SIGHUP重开文件。配置写到/etc/logrotate.d/myapp:

/var/log/myapp/*.log { daily rotate 14 maxsize 200M compress delaycompress missingok notifempty sharedscripts postrotate /bin/kill -HUP `cat /var/run/myapp.pid 2>/dev/null` 2>/dev/null || true endscript create 0640 appuser appgroup }

几个点展开说。maxsize 200M和daily配合,实现“每天切或超 200M 就切”。sharedscripts保证多个匹配文件只执行一次postrotate,避免重复发信号。postrotate里的|| true是兜底,防止 pid 文件不存在时整个轮转脚本报错退出。写完别急着等定时任务,先手动测:logrotate -d /etc/logrotate.d/myapp是 dry-run,只打印不执行,用来检查配置语法和匹配逻辑;确认无误后logrotate -f /etc/logrotate.d/myapp强制跑一次,看文件是否按预期切分、压缩、权限是否正确。

注意:logrotate默认由 systemd timer 或 cron 每天触发,手动-f测试完记得检查/var/lib/logrotate/status里的记录,避免测试打乱了正常轮转节奏。

4.3 rsyslog 规则路由与远程转发配置

本地落盘配好后,如果要集中转发,就在/etc/rsyslog.d/下加一个配置文件,比如50-forward.conf:

*.* @@logserver.example.internal:514

@@表示 TCP,@表示 UDP。生产环境一律用 TCP,UDP 丢包你根本不知道。但光这一行不够,网络抖动时会丢日志,得加磁盘缓冲队列:

*.* action( type="omfwd" target="logserver.example.internal" port="514" protocol="tcp" queue.type="disk" queue.filename="fwdq" queue.maxdiskspace="1g" queue.saveonshutdown="on" action.resumeRetryCount="-1" )

queue.type="disk"让日志先落本地队列再发,queue.maxdiskspace="1g"限制队列占用,action.resumeRetryCount="-1"表示无限重试直到成功。这套配置的意义在于:日志服务器短暂不可达时,日志在本地排队不丢,恢复后自动补发。改完systemctl restart rsyslog,然后用logger -p local0.info "test message"发一条测试日志,去日志服务器上确认收到。这个测试步骤别省,我见过配了转发但防火墙没放行、日志静默丢失好几天才发现的情况。

4.4 日志检索的常用组合与性能考量

日常排障,journalctl和grep是主力。journalctl的优势是结构化过滤,几个高频组合我列一下:按时间--since "2024-01-01 08:00" --until "2024-01-01 09:00";按服务-u nginx;按优先级-p err;实时跟踪-f;输出 JSON 便于程序处理-o json-pretty。文本日志则靠grep加awk,比如统计某接口的 5xx 数量:grep " 500 " access.log | wc -l。

性能上有个经验:先缩小范围再匹配。直接对一个几 G 的文件grep很慢,先用journalctl --since或按日期切分的文件名缩小到小时级,再grep,速度差一个数量级。另外journalctl的--no-pager在脚本里必加,否则它会调用分页器卡住。这些细节不起眼,但天天用,顺手了效率差别很大。

5. 常见问题与排查技巧实录

5.1 磁盘被日志写满的应急处理

这是最高频的事故。现象是服务报“No space left on device”,df -h一看某分区 100%。应急处理分三步:先定位大文件du -sh /var/log/* | sort -rh | head,再确认能不能删——正在被进程打开的文件直接rm不会释放空间,得用> filename清空或者重启对应服务。清空后df -h确认空间释放,然后回头改轮转策略,把根因解决掉。

排查根因时重点看两处:/etc/logrotate.d/下有没有漏配的服务,以及 journald 的SystemMaxUse有没有设。我遇到过一次是某个自定义应用日志没纳入轮转,一个月涨到 30G。教训是:新服务上线,日志轮转配置要和部署脚本一起提交,别指望事后补。

5.2 日志轮转后应用写不进去的排查

轮转后应用报权限错误,八成是create那行没配或者配错。排查顺序:ls -l看新文件属主和权限,对比应用运行用户;看logrotate配置里create的属主属组是否匹配;如果用了copytruncate,检查是不是复制过程中文件被锁。解决办法就是补上正确的create行,然后手动触发一次轮转验证。

还有一种情况是应用持有旧文件句柄,轮转后继续往已改名的文件写,新文件一直是空的。这种要么用postrotate发信号让应用重开文件,要么用copytruncate。判断方法:轮转后lsof | grep deleted看有没有进程占着已删除文件,有的话就是句柄没释放。

5.3 journald 日志丢失或查询不到的定位

journalctl查不到预期日志,先确认Storage是不是volatile——内存存储重启就没了。再看journalctl --disk-usage是否触顶被清理,SystemMaxUse设太小会导致旧日志被提前删。还有一种隐蔽情况:服务的日志输出没被 journald 捕获,比如程序自己写文件而没走 stdout/stderr,那 journald 自然看不到,得去查应用自己的日志文件。

排查时可以用journalctl -u 服务名 --no-pager | tail看最近有没有输出,没有的话检查服务的StandardOutput配置。systemd 单元里StandardOutput=journal才会进 journald,设成null或文件路径就不进。这个配置项容易被忽略,尤其是从别处拷来的单元文件。

5.4 常见问题速查表

现象可能原因排查命令处理方向
磁盘满日志未轮转或保留过多du -sh /var/log/* | sort -rh清空大文件,补轮转配置
轮转后应用报权限错create未配或属主错ls -l 日志文件补create行并验证
新日志文件为空应用持有旧句柄lsof | grep deleted加postrotate发信号
journalctl 查不到日志Storage 为 volatile 或触顶清理journalctl --disk-usage改 persistent,调大上限
远程转发丢日志无磁盘队列或网络中断查 rsyslog 队列状态配queue.type="disk"
日志检索慢单文件过大或范围太宽ls -lh看文件大小缩小时间范围,调小单文件上限

这张表我建议贴在运维手册里,出事时按图索骥比临时想快得多。但表是死的,真正管用的是平时把配置做扎实、把测试做完整。日志管理这事的本质,是在问题发生前把可观测性铺好,而不是等出事了再去翻日志。我现在的习惯是每接一台新机器,先把 journald 持久化、logrotate 模板、转发队列三件事配完,再谈别的。这套流程跑顺了,半夜被叫起来查日志的次数会明显下降。

最后分享一个小技巧:给关键服务的日志加一个每日摘要任务,用journalctl --since yesterday -p err统计错误条数,超过阈值就发提醒。这个脚本不到十行,但能让你在用户报障之前就发现问题。日志管理的价值不在于存了多少,而在于你需要的时候它在那儿、找得到、看得懂。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询