1. 日志清理这事,为什么能单独写一期
先说点实在的。服务器跑着跑着,磁盘满了,服务挂了,登录上去一看,/var/log/messages 好几个 GB,nginx 的 access.log 都大到用 cat 打开都卡半天的程度——这种场面,干过运维的都懂。
日志文件是典型的“平时没人管,出事要人命”的东西。它不会像业务代码那样在某个版本突然崩溃,而是日复一日、一点一点地蚕食磁盘空间。等你发现磁盘告警的时候,往往已经晚了半拍:数据库写不进数据了,应用报 No space left on device 了,甚至整个系统因为无法写入关键文件直接卡死。这时候你慌慌张张跑上去删几个大文件应急,表面上磁盘是腾出来了,但问题根本没解决——明天它还会满,后天它还会满,你总不能天天守在机器前面人工删吧?
所以,日志删除脚本就成了运维工具箱里的常备品。这期是“东方仙盟”系列的练气期内容,定位很明确:不讲那些花里胡哨的日志分析平台,也不谈 ELK 那套重量级方案,就踏踏实实把 Linux 环境下日志清理脚本怎么设计、怎么写、怎么部署、怎么避坑,从头到尾理一遍。适合刚接触服务器运维的初学者,也适合那些一直在手工删日志、想把自己从重复劳动里解放出来的半路出家运维。
这期的标题叫“日志删除实操脚本解析”,重点落在“实操”和“解析”四个字上。也就是说,不光是给你一个能跑的脚本就完事了,还会跟你解释每一个参数为什么这么写、每一段逻辑解决的是什么问题、哪几个地方是只有踩过坑的人才写得出来的。这样才能做到举一反三——理解了原理之后,不管你的业务日志放在哪个目录、叫什么名字、保留多少天,你都能自己改出一个符合需求的清理脚本。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 写日志清理脚本之前,先搞清楚日志是怎么增长的
2.1 磁盘空间是怎么被日志吃掉的
很多新手对日志的膨胀速度没有概念。我举个例子:一个中等流量的 Web 服务,access.log 每天可能产生 500MB 到 1GB 的数据,这还只是访问日志。如果业务再把接口的输入输出参数打到 debug 级别的日志文件里,一天几个 GB 都很正常。
更隐蔽的是那些“不常看但一直写”的日志。系统自带的 cron 日志、内核日志、认证日志,单个文件可能不大,但架不住文件多、时间长。再加上很多应用自己维护一套日志目录,比如 Tomcat 的 catalina.out、Java 服务的 logs/ 目录、Nginx 的 logs/ 目录——每个目录下都是成堆的日志文件。这些日志如果没人管,最终结果就是磁盘空间被一点一点吃干抹净。
日志增长不是线性的,往往伴随着突发流量、程序报错循环、Debug 未关等问题出现陡增。比如某天代码出了个 bug,异常信息每秒打几百条,一个下午就能把几十 GB 的磁盘塞满。所以日志清理不能是“想起来才做”,必须形成机制、定期执行。
2.2 日志轮转(logrotate)和日志删除是两码事
聊日志清理,避不开 logrotate。这是 Linux 系统自带的日志轮转工具,它的工作原理是:定期把当前日志文件重命名、压缩、再新建一个空日志文件让程序继续写。比如 /var/log/messages 会被轮转成 messages.1、messages.2.gz 等,配合 rotate 参数控制保留多少份。
很多新人会有个误解:有了 logrotate,是不是就不用手动清理了?并不是。logrotate 解决的更多是“日志文件拆分和归档”的问题,它根据配置来决定保留几份历史日志,但默认配置往往只是针对系统日志。像 Nginx、Tomcat、业务应用等自定义日志路径,logrotate 默认并不覆盖,需要你另外写配置。就算配了 logrotate,一些应用因为日志文件句柄被占用,轮转之后日志继续往旧文件里写,导致磁盘空间还在涨,这时候依然需要脚本兜底。
所以一个成熟的日志管理方案,通常是 logrotate 做第一道轮转、脚本做第二道清理兜底。这期讲的脚本,偏重的就是把“过期日志”找出来并删掉这个过程,你可以把它看作日志管理里的最后一公里。
2.3 不清理日志到底会发生什么,说几个真实事故
我见过不止一次因为日志不清理导致的事故,简单说两个。
第一个是某台应用服务器,Java 服务把日志打到 catalina.out,这个文件默认是不轮转的,只要进程不重启就一直写。结果运行了大半年,文件涨到 40 多 GB,最终磁盘 100% 打满。更要命的是,因为系统无法写入任何新文件,SSH 登录都受影响,在线排查极其痛苦。最后是重启服务、日志文件被释放后才腾出空间,但服务中断了十几分钟,这个事故本来完全可以在半小时内用一条清理命令避免。
第二个是数据库服务器。MySQL 的错误日志和慢查询日志没做清理,磁盘满之后数据库直接拒绝写入,业务方那边立刻炸锅。这种场景最尴尬的是:数据库进程还活着,但已经不可用了,你只能冒着风险去删日志。如果在磁盘还没满的时候,日志清理机制就提前把过期数据删掉,后面这些事故都不会发生。
一句话总结:日志清理不是可有可无的优化项,而是服务器运维的保底操作,是必须要在上线清单里有的东西。
3. 日志删除脚本的设计思路与核心命令选择
3.1 这一步要解决什么问题
写脚本之前先想清楚需求。日志删除脚本的核心需求非常简单:找到指定目录下、符合条件(比如超过 N 天)的日志文件,然后删掉它们。
但需求往下拆,就没那么简单了:
- 哪些目录下的日志要清理?系统日志目录?应用日志目录?
- 匹配什么文件名模式?
*.log?*.log.*?access.log-20250101这种? - 保留多少天?7 天?30 天?90 天?
- 删掉之前要不要先压缩归档?
- 是按文件的最后修改时间判断,还是按文件名里的日期判断?
- 脚本是不是要支持手动指定目录和天数?
- 要不要考虑安全机制,防止误删?
想清楚这些问题,脚本写的才有章法。否则你随手写一条 rm -rf 套个 find,一旦路径写错、通配符写错,生产环境删错文件,那就是事故级别的问题。
3.2 主力命令:find 的用法与参数详解
日志清理脚本的最核心命令就是 find。它负责按条件搜索文件,是整套脚本的“眼睛”。
拿最基础的一条命令来说:
bash复制find /var/log -name "*.log" -type f -mtime +30 -exec rm -f {} \;
这条命令做了四件事:
find /var/log:指定从/var/log目录开始搜索。-name "*.log":匹配文件名以.log结尾的文件。注意这里用的是通配符,配合引号防止 shell 自作主张扩展。-type f:只匹配普通文件,这个条件很重要。不加的话,可能会把目录、软链接等也列出来,非常容易误删。-mtime +30:匹配最后修改时间在 30 天以前的文件。
接下来是 -exec rm -f {} \;:对每个匹配到的文件执行 rm -f,{} 是 find 找到的文件名的占位符,\; 表示命令结束。
find 的几个时间参数,这里值得展开讲一下,因为这是最容易用错的地方:
-mtime:按文件内容的最后修改时间过滤,是日志清理最常用的。-ctime:按文件状态改变时间过滤,比如权限变化、硬链接数变化也会触发,常用于排查文件被改动的情况。-atime:按最后访问时间过滤,对日志文件来说参考意义不大。-mmin:按分钟来过滤,适合调试的时候用。比如-mmin +30表示 30 分钟以前修改过的文件。
-mtime +30 的语义很微妙,它并不是“正好在第 30 天被修改”,而是“最后一次修改发生在 24 到 48 小时之前的那个整数天”。实际上,find 会自动把天数乘以 24 小时来换算,所以 -mtime +30 表示的是“最后修改时间距今超过 720
