做运维这些年,最怕半夜被磁盘告警的短信吵醒。爬起来一看,不是数据库膨胀,也不是代码死循环,往往是某个服务的日志文件把磁盘啃干净了。有个客户的生产环境就出过这事,Nginx的access.log涨到20多个G,直接把根分区撑爆,前端页面全部502。当时我远程上去,df -h一敲,/ 那一行红得刺眼,再用 du -sh /var/log/nginx 一看,好家伙,全是日志。从那以后,我把自己负责的所有服务器都过了一遍日志轮转策略,该上logrotate的上logrotate,该写脚本的写脚本,再没让日志把磁盘搞崩过。
日志文件这东西,平时没人注意,出事就是大事。它不像代码bug,报错了立刻能看见;日志是慢性子,一天涨几百MB不觉得,攒一个月就是几十个G。而且很多应用进程会一直持有日志文件的句柄,你以为删了就完事,实际上磁盘空间压根没释放,必须重启进程或者用清空文件的方式处理。这篇文章我就把自己在Linux下自动管理日志文件的完整思路和实操记录下来,从logrotate配置到自写轮转脚本,从systemd journal限制到日常排查技巧,给需要的人一个能直接抄的作业。
1. 日志管理的核心痛点与方案选型
1.1 日志文件为什么会变成定时炸弹
先想明白一个问题:日志管理到底在防什么?我总结下来就三件事:磁盘空间被占满、日志文件过大导致检索困难、旧日志长期堆积泄露敏感信息。
磁盘被占满是最常见的。一台服务器上跑着Nginx、Tomcat、MySQL,每个应用都往自己的日志目录里写,系统自身的syslog、cron、secure也在写。这些文件通常只增不减,没人管的话,用不了几个月就能把磁盘写满。系统日志分区一旦满了,后果很直接:新日志写不进去、服务进程直接异常退出、甚至整个系统变得不稳定。我见过一台机器因为 /var/log 满了,sshd 都起不来,远程登录直接失败,只能去机房物理操作。
日志文件过大带来的检索问题同样让人头疼。一个20G的access.log,你想找某一天的某条请求,grep 一下要等好几分钟,甚至在机械硬盘上能把IO打满,拖垮整台服务器的其他服务。而且很多文本编辑器根本打不开这种超大文件,想手动分析都无从下手。
删日志也不是简单 rm 就完了。这里有个Linux文件系统的经典坑:如果一个进程还在往这个文件里写,你 rm 掉它,文件不会真正从磁盘上消失,因为文件句柄还开着,空间一直被占用,直到进程重启。我在排查一个磁盘明明满了但 du 却看不到大文件的问题时,才发现是一个Java进程的日志文件被误删,句柄没释放,空间全被吃住了。
1.2 主流方案怎么选:logrotate、手动脚本、journald控制
Linux下自动管理日志,成熟的路线有这么几条:系统自带的logrotate、自己写shell脚本配合crontab、针对systemd journal的限额清理。它们不是替代关系,而是各自适用的场景不同。
logrotate是Linux上最主流的日志轮转工具,几乎所有发行版都预装了。它的工作方式是定期(默认由cron驱动)检查日志文件,按你配置的规则把旧日志改名、压缩、删除。Nginx、Apache、syslog这些常见服务,装好后系统已经自带了对应的logrotate配置。它的优点是标准化程度高、配置简单、占用资源小,适合绝大多数普通日志文件。
自己写脚本的场景通常是logrotate覆盖不了的。比如某些应用日志带有特殊的时间戳后缀、需要按业务维度归档、或者需要在轮转的同时执行额外的清理操作。我之前接手过一套老系统,应用自己往固定的文件名里写日志,而且进程会持续持有句柄,用logrotate虽然有copytruncate选项可以解决,但应用那边还要求保留最近180天的日志并按照指定格式命名归档。这种定制化需求logrotate做起来很别扭,我就直接写了个脚本丢到crontab里。
systemd journal是新时代发行版自带的新日志系统。用systemd跑的服务,stdout和stderr默认都会进journal,时间长了journal文件照样会膨胀。它的优点是自带结构化日志、查询方便,但缺点也很明显:如果不设置限额,它会占用大量磁盘空间。这块需要单独配置。
我的建议是:能用logrotate解决的优先用logrotate,遇到定制化再补脚本,journal也要顺手设置好。三者搭配好了,日志管理基本就稳了。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. logrotate深入解析与配置实战
2.1 logrotate工作机制与核心配置项
logrotate本身只是个工具,真正驱动它干活的是系统定时任务。在CentOS/RHEL系里,/etc/cron.daily/logrotate 这个每日任务会在每天凌晨自动执行;在Debian/Ubuntu系则是通过 /etc/cron.daily 下的类似机制。它的配置文件分两层:主配置 /etc/logrotate.conf 定义全局默认规则,/etc/logrotate.d/ 目录下的每个文件定义具体应用的规则,后者会覆盖前者的默认值。
logrotate的配置语法不复杂,但有几个参数直接决定你的日志轮转策略是否合理:
daily/weekly/monthly:指定轮转频率。我之前默认用daily,因为生产环境日志量往往比你预期的大,按周轮转风险太高。rotate N:保留多少个轮转后的旧日志文件。比如rotate 30表示保留最近30份归档,更早的自动删除。配合daily就是保留30天。compress:轮转后使用gzip压缩旧日志。建议开启,压缩后能省大量空间,特别是文本类日志压缩比极高,通常能到10%以下。dateext:给轮转后的文件加上日期后缀。比如access.log-20250115。这个强烈建议开,不然旧日志文件名都是access.log.1、access.log.2,过一个月你根本分不清哪份是哪天的。copytruncate:先复制当前日志文件内容到归档文件,再清空原文件。这个参数对持续持有文件句柄的进程很重要,不开它的话,轮转后进程还在往已经被改名或删除的文件里写。missingok:日志文件不存在时不报错,避免每次轮转都发警告邮件。notifempty:日志文件为空时不轮转,避免产生一堆空归档。postrotate/endscript:轮转完成后执行的命令,通常用来给应用发送信号让它重新打开日志文件,比如kill -USR1 $(cat /var/run/nginx.pid)。
2.2 按大小轮转和按时间轮转怎么选
logrotate默认只支持按时间轮转:daily、weekly、monthly。但很多场景下按时间轮转并不合理。比如某个接口平时没人访问,access.log一个月才涨几百KB,你每天轮转一次纯属浪费;反过来某个高流量服务一天写几个G,按天轮转都有风险扛不住,恨不得每小时切一次。
logrotate没有内置的按大小轮转选项,但它有个取巧的玩法:用 size 参数叠加时间条件。比如这样配置:
bash复制/var/log/nginx/access.log {
daily
size 100M
rotate 30
compress
dateext
copytruncate
missingok
notifempty
}
这里的逻辑是:每天执行一次检查,但只有当前日志文件大小超过100M才真正执行轮转。也就是说,日志量小的时候可能好几天才轮转一次,日志量大的时候每天都会切。这个组合在实际运维里非常好用,既控制了单文件体积,又不至于频繁轮转产生大量空文件。
如果日志量实在巨大,单日超过几个G,daily + size 就不够用了。这种场景应该让应用层自己按小时切分日志,或者用我后面会讲的自写脚本来做更高频的轮转。logrotate的最小调度单位是天,因为它依赖cron的每日定时任务,想做到每小时轮转它管不了。
2.3 多份日志的不同轮转策略配置示例
生产环境往往多个服务跑在同一台机器上,它们的日志特点不同,轮转策略也得分而治之。我以一台跑Nginx和Java应用的服务器为例,给出实际在用的配置。
Nginx的访问日志和错误日志分开配,访问日志量大但价值密度低,错误日志相对小但更重要,保留时间可以更长:
bash复制# /etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily
size 100M
rotate 30
compress
dateext
copytruncate
missingok
notifempty
sharedscripts
postrotate
[ -f /var/run/nginx.pid ] && kill -USR1 $(cat /var/run/nginx.pid)
endscript
}
这里用了 sharedscripts,意思是多个日志文件匹配时,轮转完只执行一次脚本,而不是每个文件都执行一次。Nginx收到USR1信号会重新打开日志文件,配合 copytruncate 双保险,基本不会出现轮转后日志写入丢失的问题。
Java应用的日志更麻烦。很多Java服务用Logback或Log4j2,默认配置下进程会一直持有日志文件句柄。如果直接用logrotate改名,进程还往旧文件里写。对这类日志,配置里就不能用默认的rename方式,必须用 copytruncate:
bash复制# /etc/logrotate.d/myapp
/opt/myapp/logs/app.log {
daily
rotate 14
compress
dateext
copytruncate
missingok
notifempty
}
copytruncate 有个小缺点:复制和截断之间有一个极短的时间窗口,这几毫秒内写入的日志会丢。不过对于绝大多数业务场景,丢几行日志的代价远小于磁盘写满,这个trade-off完全值得。
系统自身的messages、secure这些日志,发行版已经配好了默认轮转规则,一般不用动。但要注意检查一下,有些精简过的系统镜像没装logrotate,或者配了但没有生效,坑得很。
3. 手动脚本方案:当logrotate不够用的时候
3.1 什么时候需要自己写脚本
logrotate虽然强大,但有些场景它就是没法用,或者用起来很难受。我遇到过的典型情况有这几种。
第一种是按小时的滚动归档。我们有个数据采集服务,日志量极其夸张,一天能写8个G,按天轮转意味着单个日志文件涨到8G才切一次,运维上完全不可接受。logrotate的最小粒度是天,所以我写了个脚本放在crontab里,每小时执行一次,把当前日志复制出来归档并清空原文件。
第二种是日志需要按业务维度做额外处理。比如我们有个订单系统的日志,需要按天归档后同步到对象存储,同时本地只保留7天。logrotate的postrotate虽然能执行命令,但要处理同步失败重试、归档命名规则等逻辑,写在logrotate配置里会变得非常臃肿难维护。
第三种是应用自己不遵循常规日志文件命名规范。有些老系统日志文件名带着PID或者奇怪的固定后缀,logrotate的通配符匹配容易出问题。这种还是自己写脚本控制起来最放心。
3.2 一个可靠的日志切割脚本怎么写
自己写日志轮转脚本,核心要解决三个问题:文件句柄不释放、归档命名冲突、并发执行冲突。下面是我在用的一个脚本模板,你可以在自己的环境下改改路径直接用:
bash复制#!/bin/bash
# 日志切割脚本示例
# 需要切割的日志文件路径
LOG_FILE="/opt/myapp/logs/app.log"
# 归档目录
ARCHIVE_DIR="/opt/myapp/logs/archive"
# 保留归档的天数
RETENTION_DAYS=7
# 当前时间戳,用于归档命名
STAMP=$(date +%Y%m%d%H%M%S)
ARCHIVE_FILE="${ARCHIVE_DIR}/app.log.${STAMP}"
# 创建归档目录
mkdir -p "${ARCHIVE_DIR}"
# 复制当前日志到归档文件(cp + truncate,解决文件句柄问题)
cp "${LOG_FILE}" "${ARCHIVE_FILE}"
# 清空原文件,不释放句柄,进程不需要重启
: > "${LOG_FILE}"
# 压缩归档文件(可选,文本日志压缩率很高)
gzip "${ARCHIVE_FILE}"
# 删除超过保留期限的归档文件
find "${ARCHIVE_DIR}" -name "app.log.*.gz" -mtime +${RETENTION_DAYS} -delete
这段脚本的核心逻辑就三步:复制当前日志内容到归档、清空原文件、清理过期归档。cp 再 truncate 的方式等效于logrotate的 copytruncate,进程不需要感知日志被轮转过,依然往同一个文件路径写,不会出现句柄指向已删除文件的问题。
很多新手写这个脚本时会直接用 mv 把原文件改名,然后通知进程重新打开。这个做法在Nginx、syslog这类支持信号重开的服务上没问题,但对Java等普通应用就行不通了,进程根本不会去重新打开文件。所以对于不熟悉的应用,优先用cp + truncate方案,无侵入、安全。
清理过期归档我用的是 find -mtime +7,它的含义是找到修改时间超过7天的文件并删除。注意 -mtime +7 和 -mtime 7 的区别:前者是超过7天,后者是刚好7天。实际使用中一定要用 + 号,不然边界情况会漏删或误删。
3.3 配合crontab实现无人值守
脚本写好了,接下来把它挂到系统的定时任务里。crontab的写法有讲究,我直接给例子:
bash复制# 编辑当前用户的crontab
crontab -e
# 每小时的30分执行一次日志切割
30 * * * * /opt/scripts/rotate_log.sh >> /var/log/rotate_log_cron.log 2>&1
注意我这里把脚本输出重定向到了日志文件里。为什么?因为crontab任务如果产生输出,系统会把输出通过邮件发给当前用户,服务器上根本没配邮件服务,这些输出就丢了。如果脚本执行异常,你很难发现。把输出重定向到文件,排查问题时直接看这个文件就行。
还有一个容易被忽略的点:脚本开头一定要用 #!/bin/bash,并且在crontab里写绝对路径。cron执行环境是一个精简的shell环境,PATH路径往往不包含 /usr/local/bin,如果你的脚本里用到了某个非标准路径下的命令,直接执行没问题,但通过cron跑就会报 command not found。稳妥做法是脚本里所有涉及外部命令的都写绝对路径,或者开头先 export PATH=/usr/local/bin:/usr/bin:/bin。
脚本权限也要注意。放在 /opt/scripts 下的脚本,记得 chmod +x rotate_log.sh,并且确认crontab的用户对这个目录和文件有读写权限。我见过有人把脚本放在自己用户目录下,然后crontab用root跑,结果权限不够,脚本执行不完整,日志切到一半就停了。
4. systemd journal日志的管理与限制
4.1 journald与文件日志的区别
现在的Linux发行版基本都用systemd管理服务,随之而来的就是journald日志系统。它跟传统的文件日志有本质区别:journald把日志以二进制格式写入journal文件,不是纯文本,你在终端用 tail 直接看不了,必须通过 journalctl 命令查询。很多新手以为journald占了磁盘空间就直接删 /var/log/journal/ 目录下的文件,我见过有人这么干把服务直接搞挂的,这做法极其危险。
journald的日志来源也很特殊。凡是systemd管理的服务,进程的输出——不管stdout还是stderr——都会被journald自动捕获。也就是说,你不需要让Java应用写一个 app.log 文件,它的systemd ouput本身就会进journal。这在调试问题上很方便,journalctl -u myapp.service 直接能看到服务的完整输出,比翻文件好用得多。
但方便归方便,journal文件的膨胀速度非常快。特别是那些debug级日志打得多、写日志又很频繁的服务,journal可能几天就涨到好几G。而且journal文件本身还带索引,膨胀之后查询也会变慢,所以必须主动限制它的体量。
4.2 通过journalctl查看和清理日志
journalctl是操作journal的唯一入口。日常用的几个命令我列一下:
bash复制# 查看所有日志(默认显示本次启动以来的)
journalctl
# 查看指定服务的日志
journalctl -u nginx.service
# 查看最近30分钟日志
journalctl --since "30 min ago"
# 查看指定时间段的日志
journalctl --since "2025-01-15 00:00:00" --until "2025-01-15 12:00:00"
# 显示最后50行并持续跟进(类似tail -f)
journalctl -u myapp.service -f
# 查看占用空间最多的服务
journalctl --disk-usage
手动清理journal的方式有三种。第一种是 journalctl --vacuum-time=7d,清理7天之前的日志;第二种是 journalctl --vacuum-size=500M,把journal总占用压缩到500MB以内;第三种是 journalctl --vacuum-files=5,只保留最近5个journal文件。这三个指令我实际用得最多的是 --vacuum-size,直接粗暴,限定总容量,超出就删最老的。
4.3 持久化配置与关键参数调整
手动清理只是临时缓解,真正的长效机制是修改journald的配置文件,让它自己控制体积。journald的配置文件在 /etc/systemd/journald.conf,核心参数有这几个:
ini复制# 限定journal最大占用500M
SystemMaxUse=500M
# 单文件最大64M
SystemMaxFileSize=64M
# 文件超过一定时间强制轮转
MaxRetentionSec=7day
配置完记得重启journald服务:
bash复制systemctl restart systemd-journald
这几个参数里最关键的是 SystemMaxUse。它决定了journal最多占用多少磁盘空间,超过之后journald会自动清理最旧的日志,不需要你干预。生产服务器我一般建议设置成512M到1G,够排查看近期问题用,又不会吃掉太多磁盘。
还有一个细节:journald默认的日志存储方式在 /run/log/journal/,这个目录是tmpfs,重启机器数据就没了。如果你希望日志持久化,需要手动创建 /var/log/journal/ 目录,journald检测到存在就会切换过去。生产环境还是建议开持久化,不然机器一重启,想看崩溃之前的关键日志什么都捞不着。
5. 日志轮转的测试与验证
5.1 手动触发logrotate测试
写完logrotate配置,千万记得测试,不能直接丢在那等明天cron跑。logrotate支持debug模式和强制执行模式,这两个模式是我测试时必用的:
bash复制# 调试模式:不真正执行轮转,只打印会做什么操作
logrotate -d /etc/logrotate.d/nginx
# 强制模式:即使还没到轮转时间也强制执行
logrotate -f /etc/logrotate.d/nginx
debug模式会详细列出配置解析结果和计划执行的动作,比如准备给哪个文件改名、压缩哪个文件、执行哪个脚本。如果配置写错了,比如路径写错、参数不识别,这个命令会直接抛错,你可以提前发现。
强制模式适合验证真实效果。跑完 logrotate -f 之后,去日志目录看一眼:
bash复制ls -lh /var/log/nginx/
正常情况下应该能看到 access.log 和类似 access.log-20250115.gz 的归档文件。再用 cat 或 less 看归档文件内容是否完整,确认日志数据没有丢失。
5.2 模拟大日志场景验证脚本
自写脚本的验证不能光靠小文件测一遍就完了,我遇到过一次脚本在小日志下正常、大日志下出问题的案例。那次是日志文件有6个G,cp 复制时占用了大量磁盘IO,加上同时还在写入,复制出来的归档文件不完整,而且磁盘一直被占满。所以验证脚本时尽量模拟大文件场景。
我当时的做法是:先用 dd 命令快速生成一个大文件模拟日志:
bash复制# 生成一个2G的测试日志文件
dd if=/dev/zero of=/opt/myapp/logs/app.log bs=1M count=2048
然后手动执行脚本,观察三件事:
- 脚本执行耗时多久,是否在可接受范围内
- 归档文件是否完整,大小是否接近原日志
- 脚本执行期间是否影响了服务正常写入
第一次跑的时候我就发现,2G的文件 cp 一遍大概要几十秒,期间磁盘IO飙高,服务写入有轻微卡顿。后来我把脚本改成了先用 mv 改名,再 cp 零字节文件到位,然后让进程重新打开文件的方式,但这要求进程支持信号重开,只有部分应用适用。对不支持的应用,只能接受这几十秒的IO开销,或者把轮转频率调高,让单次日志量变小。
5.3 观察轮转后的日志是否正常写入
轮转完成后,最重要的验证是确认应用还在正常写日志。你可以在轮转后等几分钟,然后看主日志文件的大小是否在持续增长:
bash复制# 每隔几秒查看一次文件大小,确认在增长
watch -n 2 'ls -lh /opt/myapp/logs/app.log'
做这一步的原因很现实:很多应用不会自动重新打开日志文件句柄。如果你用的是 mv 改名方案,轮转后应用还在往已经移走的旧文件里写,新日志文件永远不增长。这种问题当时不查,等你想看最新日志的时候才发现什么数据都没有。
更好的验证方式是实际打一条日志。比如Nginx服务,手动请求一个接口,然后看access.log里有没有对应记录。如果应用是Java,可以在应用的日志界面或者通过其他方式触发一条日志,确认写到了新文件里。这一步虽然简单,但比任何花哨的检查都管用。
6. 常见问题与排查技巧实录
6.1 日志轮转后文件句柄未释放
这是日志管理里翻车率最高的问题。表现是:logrotate执行了,旧日志也改名压缩了,但磁盘空间不降反升,或者主日志文件被删除后空间一直不释放。
排查这类问题用这个命令最直接:
bash复制# 找出被删除但仍被进程占用的文件
lsof | grep deleted
如果输出里有类似 java ... /opt/myapp/logs/app.log (deleted) 的行,说明Java进程还持有这个文件的句柄。就算你 rm 了文件,空间也没有真正释放,必须重启进程或者让进程重新打开文件句柄。
解决办法两种:第一种是配置logrotate加 copytruncate,用复制加截断的方式替代改名,进程不需要感知文件变化;第二种是postrotate里给进程发信号让它重新打开日志文件。注意这两种方式并不冲突,很多生产配置是两个都上,最大化兜底。
6.2 轮转太频繁导致日志丢失
有些运维新手为了省磁盘,把轮转频率设得极端高,比如每小时切一次,只保留一天。结果就是出问题时想查昨天的日志,发现已经没了。日志管理的目标不是把日志全部删光,而是做到“够查这几天的问题、又不撑爆磁盘”之间的平衡。
我给的参考基线:普通业务日志,每天轮转一次,保留30天压缩归档,单文件超过100M也触发轮转。这个组合对绝大多数场景都够用。如果日志量极大,优先考虑按小时轮转,但保留天数不能低于3天。对高价值日志(比如订单、支付、用户操作日志),保留期至少90天,而且建议同步一份到异地存储。
6.3 磁盘告警但日志文件不大
处理过一次很诡异的问题:磁盘告警90%,但 du -sh /var/log 一看只有几十MB,根本不算大。后来查了半天,发现是一个被删除但没有释放句柄的文件在占地方。这种场景用 lsof | grep deleted 一查就露馅了。
另一种情况是 du 和 df 结果不一样。du 统计的是文件内容实际大小,df 看的是分区使用情况。如果一个文件被删了但句柄没释放,du 看不到它,df 却会显示空间被占用。所以排查磁盘问题时要两个命令一起看,光看 df 容易漏掉真相。
6.4 压缩进程占用CPU过高
logrotate默认用gzip压缩旧日志,日志量大的时候,压缩操作会占用不少CPU。我遇到过Nginx日志轮转后gzip压缩一个2G的文本文件,直接把双核VPS的CPU跑满了,导致业务响应变慢。
遇到这种情况,可以考虑把压缩级别调低,或者干脆关闭压缩。logrotate的 compresscmd 和 compressoptions 参数可以指定压缩命令和参数:
bash复制compress
compresscmd /usr/bin/gzip
compressoptions -1
-1 是gzip的最快压缩级别,压缩率低一些但速度快很多。如果你的日志主要是为了排查看近期问题,而不是长期审计归档,其实可以不压缩,反正保留几天就删了,省下CPU更划算。
7. 日志管理配置速查表与扩展建议
7.1 核心配置项对照速查
为了让你不用翻前面的内容就能快速上手,我把常用的配置项按照“功能-参数-说明”整理成了速查表:
| 功能 | 参数 | 建议值 | 说明 |
|---|---|---|---|
| 轮转频率 | daily / weekly / monthly | daily | 生产环境默认按天 |
| 按大小触发 | size | 100M | 超过阈值触发轮转 |
| 保留归档数 | rotate | 30 | 配合daily保留30天 |
| 压缩归档 | compress | 开启 | 文本日志压缩率高 |
| 日期后缀 | dateext | 开启 | 归档文件名带日期 |
| 句柄处理 | copytruncate | 开启 | 解决进程持有句柄问题 |
| 空文件跳过 | notifempty | 开启 | 避免产生空归档 |
| 缺失跳过 | missingok | 开启 | 文件不存在不报错 |
| 执行后脚本 | postrotate | 按需 | 通知应用重开日志句柄 |
7.2 监控日志大小并主动告警
日志轮转配置好了,不代表可以高枕无忧。我还会配合一个简单的监控脚本,检查日志文件大小和磁盘使用率,超出阈值就告警,把问题消灭在萌芽阶段。
bash复制#!/bin/bash
# 日志磁盘使用监控脚本
THRESHOLD=80
# 检查根分区使用率
USAGE=$(df / | awk 'NR==2 {print $5}' | sed 's/%//')
if [ "$USAGE" -gt "$THRESHOLD" ]; then
echo "$(date): 磁盘使用率 ${USAGE}% 超阈值" >> /var/log/disk_monitor.log
# 这里可以接入告警通道,如企业微信机器人、短信、邮件等
fi
这个脚本虽然简单,但实际价值很高。磁盘告警不是可以事后补救的事,它必须前置。有了这个监控,日志轮转即使因为各种原因没执行成功,至少你能第一时间接到告警,而不是等服务挂了你才知道。
7.3 结合应用自身的日志滚动机制
最后说一个很重要的思路:很多现代应用自带日志滚动能力,比如Java的Logback、Log4j2的 RollingFileAppender,Python的 logging.handlers.RotatingFileHandler,Node.js的 pino-roll。这类应用在应用层就能自动切分日志文件,而且可以精确控制单文件大小、保留数量,比外部logrotate更靠谱。
那是不是应用自带了日志滚动,就不需要logrotate了?不完全是。应用自带的滚动只处理它自己的日志文件,系统日志(syslog、secure)和应用写出去的非标准日志,还是要靠logrotate来管。理想的状态是:应用层处理它自己最了解的那部分日志(格式、路径、保留策略),logrotate兜底整个系统的日志。两者配合,而不是二选一。
我之前维护的一个Java服务,自己用Logback配置了按天滚动,保留30天,压缩归档。同时这台机器上还有其他Python脚本和系统服务,我用logrotate统一管它们的日志。两边各自发力,互不冲突,磁盘再也没被日志干崩过。
日志管理这件事吧,做的时候感觉就是写几行配置、挂个定时任务,不起眼,但是真到出事儿的时候,就知道它有多值钱。我现在每次新上服务器,第一件事就是检查日志轮转配置,顺手把监控脚本挂上,花十分钟,换以后的安稳觉。最后再分享一个小技巧:logrotate的配置文件每个服务独立放在 /etc/logrotate.d/ 下,命名清晰,千万别图省事把所有规则堆在同一个文件里,不然后面排查问题的时候想死的心都有。
