使用Perl正则替换清理Apache access_log后日志停止记录的问题求助
嘿,这个问题我之前踩过坑!核心原因其实是Perl的-i原地编辑参数和Apache的日志写入机制冲突了,咱们一步步说清楚:
为啥日志会停止记录?
Apache运行时会一直握着access_log的文件句柄,所有新日志都是通过这个句柄写入文件的。而Perl的-i参数工作逻辑是:
- 先创建一个临时文件
- 把处理后的内容写入临时文件
- 删除原来的access_log,再把临时文件重命名成access_log
这时候尴尬的事情来了:Apache手里的文件句柄还是指向那个已经被删除的旧文件(系统里看不到,但进程还占着它),新日志其实一直在写,但都进了这个“隐形”的旧文件里,直到你重启Apache,它才会重新打开新的access_log文件句柄,你才能看到正常的日志输出。
怎么解决这个问题?
给你几个适合做成cron定时任务的实用方案:
用日志轮转工具配合处理(首推)
别直接碰正在写入的access_log!用Linux系统自带的logrotate(大部分发行版默认预装),配置它定期轮转日志(比如每天一次)。轮转完成后,logrotate会自动通知Apache重载配置(执行reload而非restart),让Apache切换到新的日志文件。这时候你再用Perl去处理那些已经轮转出来的旧日志(比如access_log.1、access_log.2.gz这类),完全不会影响当前的日志记录。手动处理前先让Apache切换日志
如果不想用logrotate,每次处理日志前先让Apache重新打开日志文件句柄:# 先重载Apache,让它切换到新的日志文件(不会中断服务) sudo systemctl reload apache2 # 也可以用 apachectl graceful # 现在处理的是已经被Apache“抛弃”的旧日志文件,放心操作 perl -pi -e "s/.*( 301 | 404 |index\.php\/|Googlebot| \/ |bingbot|Special\:CreateAccount|php\?title\=|POST \/index.php|xlsx.full.min.js|javascript.js| \/\?author\=|Baiduspider).*\n//" access_log不过要注意,如果cron任务执行太频繁,频繁reload Apache会有轻微性能影响,还是日志轮转更稳妥。
避免原地编辑,改用输出到新文件
放弃-i参数,把处理后的内容写到新文件,再替换原文件(替换前必须先reload Apache):sudo systemctl reload apache2 # 直接跳过匹配行,比正则删除更直观,效果一致 perl -e 'while(<>) { next if /( 301 | 404 |index\.php\/|Googlebot| \/ |bingbot|Special\:CreateAccount|php\?title\=|POST \/index.php|xlsx.full.min.js|javascript.js| \/\?author\=|Baiduspider)/; print; }' access_log > access_log.clean mv access_log.clean access_log
另外提个小细节:你的第一个Perl正则里的\d+.\d+.\d+.\d+要注意,.是正则里的任意字符,应该写成\d+\.\d+\.\d+\.\d+(转义点号),不然会匹配到比如123a45b67c89这种非IP的内容哦!
备注:内容来源于stack exchange,提问作者Alan

