这篇博文以课程课件为蓝本来探讨logrotate和自动化日志处理的一系列课题,细节和深层次原理部分略有删减,是一篇被课程耽误了的技术博文。既然谈的很直白,是一篇被课程耽误了的技术博文,如若有打着博客做引导或者拿着开源工具不开源之类的讨伐和道德绑架,恕不回复。
站在用户的角度思考问题,与客户深入沟通,找到曲阜网站设计与曲阜网站推广的解决方案,凭借多年的经验,让设计与互联网技术结合,创造个性化、用户体验好的作品,建站类型包括:网站设计制作、成都网站建设、企业官网、英文网站、手机端网站、网站推广、域名申请、虚拟主机、企业邮箱。业务覆盖曲阜地区。这里是分割线,不废话了,直接切入正文,对课程有兴趣,想深入理解logrotate的朋友可以关注文末课程介绍:)
日志切割是指当应用或系统的日志文件达到设定的触发条件(如按照一定的时间周期:每天,按照大小:500MB),对其进行切割/分割处理,类似截断处理,把原本容量比较大的日志文件“劫走”转存为另外一个日志文件留存归档,这一刻之后产生的日志,继续输出到文件头被重置为0的日志文件中。
变化的部分:日志文件的容量(瘦身变小),日志文件的个数(多出一份被切割下的历史日志)
不变的部分:日志文件名不变
此外,一段时间后,我们还需要删除时间久远的日志文件,整个过程也被俗称为日志滚动(log rotation)。
在线应用(包括操作系统)在长期运行过程中,会产生很多过程日志记录,通常是应用程序记录的一些对系统管理员或者程序开发者有用的信息的文件,诸如正在执行什么、发生了什么错误等一系列信息。
随着日志记录的不断积累,日志文件越来越大,随着时间推移,会带来以下弊病:
一个栗子:
日志切割的基本需求:
应用层面
切割过程中不影响应用的正常运行(不能停用应用来分割日志)
数据层面
不丢失日志,或者在可接受的范围内丢失极少日志
切割过程中不影响应用继续输出记录日志(日志文件名不变)
日志容量层面
切割后新的日志文件从空文件开始重新记录(容量和文件头被重置),便于后续查询使用
日志归档层面
切割后老旧日志方便归档压缩处理(文件名加上日期后缀等)
分割后的老旧日志可按照保存周期轮询删除
管理维护层面
自动化周期性进行,周而复始
从以上需求出发,在满足不停用应用的首要前提下,可以设计出两种日志切割的基本思路:
思路1--重命名移走旧的日志文件,同时生成一个新的日志文件(文件名与切割之前保持一致)
把现有日志mv成另外一个文件,同时自动生成一个同文件名(mv之前的日志文件名)的新日志文件
日志的文件名不变,但是需要确保应用可以指向新的文件句柄
新的日志文件当然从0开始写,日志文件成功瘦身!
思路2-拷贝并重命名一份现有日志文件,同时把现有容量大的日志文件内容清空
把现有日志文件cp一份为另外的文件名,同时非常快速地把现有日志文件清空
但不改变现有日志文件的文件句柄(在3.1.1章节会有更多详述)。
这样日志文件的文件名和句柄都没有变化,只是内容已经被清空,也是从0开始继续写入,减肥成功!
最后,需要结合定时任务,周期性定时执行日志切割任务,自动化处理
自定义脚本切割日志,核心原理是mv已有的日志文件,然后生成一个新的日志文件,结合kill
-USR1
PID 来reload应用,以便应用获取新日志文件的文件句柄,日志即可输出到新的日志文件。
注意:
-USR1 PID 仅仅是reload应用配置,不会真正重启应用进程,因此不会引起应用停止运行脚本切割nginx日志栗子:
#/bin/bash
bakpath='/home/nginx/logs'
logpath='/var/log/nginx/logs'
if [ ! -d $bakpath/$(date +%Y)/$(date +%m) ];then
mkdir -p $bakpath/$(date +%Y)/$(date +%m)
fi
mv $logpath/access.log $bakpath/$(date +%Y)/$(date +%m)/access-$(date +%Y%m%d%H%M).log
#给nginx发送一个信号量,让nginx重载,重新生成新的日志文件
kill -USR1 `cat /usr/local/nginx/logs/nginx.pid`
log4j是apache针对java应用开源的一套日志框架,java应用可以通过加载指定的jar包,结合配置文件,从应用本身规范日志输出的格式,级别等,同时可附加实现日志的切割处理。
切割的触发条件可以是时间周期和日志大小两个维度。
log4j一般需要开发人员的协助,最好由开发人员直接实现。
linux系统自带的日志处理工具,功能非常强大,可以进行日志的切割,压缩,滚动删除等处理。
自身基于系统的crontab来运行,无需手动配置。
cronolog开源的日志处理工具,可以实现自动的按规则生成周期性的日志文件,需要单独安装后配置使用。
对比方案 | 部署工作量 | 对应用的亲和性 | 功能性 |
---|---|---|---|
脚本 | 前期工作量较大 底层逻辑都需要自己逐一实现 功能完善的脚本开发量较大 | 一般 | 看脚本本身 |
log4j | 较小,加载jar包进行配置即可 | 好 | 较为强大,但不支持日志压缩 |
第三方工具 | 较小仅仅需要配置 | 较好 | 强大 |
这里的第三方工具仅仅针对logrotate而言(其它工具可以参考对比因素来对比)。
选型:
多用拿来主义,避免自己重复造轮子(特指自己巴拉巴拉一股脑式写脚本)
优选应用层面结合log4j方案
建议应用层面可以应用log4j的就应用log4j,结合第三方工具进行日志压缩和周期性删除处理
备选(次选)开源第三方工具--logrotate
不便于应用log4j的情况下推荐使用第三方工具,尤其是logrotate,系统自带,开箱即用--次选方案
操作系统为每一个进程维护了一个独立的打开文件表(fdtable),进程每新打开一个文件,表中就会增加一个条目。
文件描述符是一个整数,代表fdtable中的索引位置(下标),指向具体的struct file(文件句柄/文件指针)
文件句柄(文件指针)对应着文件的详细信息,存储着文件的状态信息、偏移量和文件的inode信息等。
文件句柄中存储的inode信息,对应到应用进程正在写的一个具体文件
每一个文件描述符会与一个打开的文件(文件句柄)相对应
综上所述,文件句柄可以唯一界定操作系统中一个特定的文件。
推理:
文件句柄不包括文件路径和文件名,因此文件路径和文件名的变化,不会引起对文件句柄的修改,进而不会引起应用进程对某一特定文件(文件句柄的具体指向)的写操作
可以通过实验来验证上述推理结论。
文件句柄的存在,决定了logrotate进行日志切割时候,存在以下两种方案:
- ##### 切割后使用新的文件句柄--create方式
create方式也是默认的方案,它的核心思路是重命名原日志文件,并创建新的日志文件。create方案是在mv+create 执行完之后,通知应用重新在新的日志文件写入即可。
那么如何通知应用程序重新打开日志文件,从而往新的空日志文件中写入呢?
简单粗暴的方法是杀死进程重新打开。但是这样会影响在线业务,不可取!
于是有些程序提供了重新打开日志的接口,以Nginx为例,是通过发送USR1信号给Nginx进程来通知Nginx重新打开日志文件的。也存在一些其他方式(如IPC),前提是程序自身要支持。
不过,有些程序并不支持create方式,压根没有提供重新打开日志的接口;而如果粗暴地重启应用程序,必然会降低可用性,为此引入了copytruncate的方案。
这个方案的思路是把正在输出的日志拷(copy)一份出来重命名,再清空(trucate)原来的日志。从结果上看,旧的日志内容存在滚动的文件里,新的日志输出到被清空的文件里。
以上两种方案中,能用默认的create方案就不用copytruncate,why?
数据丢失风险
这里关于create和copytruncate方式的具体执行过程,以及注意事项,包含了较为关键的技术细节。如若以上描述不足以理解,可以关注课程https://edu.51cto.com/sd/3f309, 接受免费试看。
logrotate自身已经被集成到系统定时任务中,基于CRON来运行,默认每天运行一次。
定时任务:
/etc/cron.daily/logrotate
#!/bin/sh
/usr/sbin/logrotate -s /var/lib/logrotate/logrotate.status /etc/logrotate.conf
EXITVALUE=$?
if [ $EXITVALUE != 0 ]; then
/usr/bin/logger -t logrotate "ALERT exited abnormally with [$EXITVALUE]"
fi
exit 0
要点:
/usr/sbin/logrotate 执行文件
-s /var/lib/logrotate/logrotate.status 记录执行后的状态
/etc/logrotate.conf 运行时加载的配置文件
问题: cron.daily究竟是在每天什么时间执行的呢?
Logrotate是基于CRON运行的,所以这个时间是由CRON控制的,具体可以查询CRON的配置文件/etc/anacrontab(老版本的文件是/etc/crontab)
cat /etc/anacrontab
# /etc/anacrontab: configuration file for anacron
# See anacron(8) and anacrontab(5) for details.
SHELL=/bin/sh
PATH=/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=root
# the maximal random delay added to the base delay of the jobs
RANDOM_DELAY=45 #这个是随机的延迟时间,表示大45分钟
# the jobs will be started during the following hours only
START_HOURS_RANGE=3-22 #这个是开始时间段,3点-22点
#period in days delay in minutes job-identifier command
1 5 cron.daily nice run-parts /etc/cron.daily
7 25 cron.weekly nice run-parts /etc/cron.weekly
@monthly 45 cron.monthly nice run-parts /etc/cron.monthly
1 5 cron.daily
第一个是Recurrence period 第二个是延迟时间(the base delay,基本的延迟时间)
总的延迟时差是:基本延迟+随机延迟=5~(5+45),即5-50 min
开始的基准时间:3-22点,不出意外,就是3点开始
所以cron.daily会在3:00+(5,50)这个时间段执行
定时任务执行时间,也可以通过日志和处理过的日志文件两种方式来确认。
logrotate自动化处理日志的过程如下:
Redhat系列系统缺省的cron 在每天的3:00+(5,50)这个时间段唤醒触发cron.daily下定义的logrotate定时任务
logrotate加载默认的配置文件/etc/logrotate.conf,定位判断需要被处理的日志文件
基于配置文件的相关参数,对匹配到的日志文件执行日志切割、压缩、转储即周期性删除处理
完成后产生过程状态记录文件 /var/lib/logrotate/logrotate.status
实战运用中,我们是先设置好logrotate的相关参数和任务,然后等待cron来唤醒定时任务触发执行。
注意:
理解了原理过程,我们再来梳理一下logrotate的配置文件。配置文件分为全局默认配置/etc/logrotate.conf和/etc/logrotate.d目录下的自定义配置。
配置的有效性和优先级:
logrotate加载配置时,会针对/etc/logrotate.d目录下的每个自定义配置,与全局默认配置/etc/logrotate.conf进行合并渲染,如存在相同项或者冲突项,以自定义配置为准:
相同项:
全局配置和自定义配置中配置了相同的参数key,但value不同的情况
冲突项:
全局配置和自定义配置中存在的冲突项
全局配置文件:
cat /etc/logrotate.conf
# see "man logrotate" for details
# rotate log files weekly
weekly #每周轮转一次
# keep 4 weeks worth of backlogs
rotate 4 #保留四个被切割转储后的日志文件(即备份的老日志文件)
# create new (empty) log files after rotating old ones
create #rotate后,创建一个新的空文件,默认的create方式处理
# use date as a suffix of the rotated file
dateext # 日志文件切割时添加日期后缀,后缀格式YYYYmmdd,切割后的文件名如xxx.log-20200202
# uncomment this if you want your log files compressed
#compress #默认切割转储后的日志是不压缩的
# RPM packages drop log rotation information into this directory
include /etc/logrotate.d #编程中常用的include大法,该目录下的配置文件都会被引用生效
# no packages own wtmp and btmp -- we'll rotate them here #顺带rorate两个孤儿日志
/var/log/wtmp {
monthly
create 0664 root utmp
minsize 1M
rotate 1
}
/var/log/btmp {
missingok
monthly
create 0600 root utmp
rotate 1
}
# system-specific logs may be also be configured here.
默认配置文件中已经通过include /etc/logrotate.d大法指明了自定义配置文件的路径,因此在这个路径下定义细化配置即可。
例1: create方式切割nginx日志
cat /etc/logrotate.d/nginx
/var/log/nginx/*.log { # 可以指定多个路径,多个路径通过空格或换行符分隔,支持正则匹配
daily # 日志轮询周期,weekly,monthly,yearly
rotate 30 # 留存30份切割后的旧日志,以天为周期,即保存30天旧日志,超过则删除
size +100M # 超过100M时分割,单位k,M,G,优先级高于daily
compress # 切割后立即对老日志进行gzip压缩,也可以为nocompress
dateext # 日志文件切割时添加日期后缀
missingok # 如果没有日志文件也不报错
notifempty # 日志为空时不进行切换,默认为ifempty
create 640 nginx nginx # 使用该模式创建新的空日志文件,mode,user和group参数可以省略
sharedscripts # 所有的文件切割之后再一次性执行下面脚本
postrotate
if [ -f /var/run/nginx.pid ]; then #脚本符合shell语法和逻辑即可
kill -USR1 `cat /var/run/nginx.pid`
fi
endscript
}
其它较为常用的配置参数:
nocopytruncate copy日志文件后不截断不清空日志,仅仅备份用
nocreate 不建立新的日志文件,用于仅仅只需要对应用日志进行压缩和轮询删除的场景
errors address 遇到错误时信息发送到指定的Email 地址
olddir directory 转储后的日志文件放入指定的目录,必须和当前日志文件在同一个文件系统
prerotate 在logrotate转储之前需要执行的指令,例如创建一个转储的日志目录
rotate count 指定日志文件删除之前转储的次数(个数),0指没有备份,5指保留5个备份
maxage 以单个转储时间周期为计数单位来保留老旧日志,如每天处理,代表保留多少天的老旧
日志,此时与rotate count无区别
dateext 使用当期日期-YYYYmmdd作为转储后的日志文件附加后缀,不配置则以数字1到n作为后
缀,n为rotate n中的配置参数
dateformat .%s 配合dateext使用,紧跟在下一行出现,定义文件切割后的文件名附加后缀,必须配合
dateext使用,只支持 %Y %m %d %s 这四个参数
其它可参照man文档,以及/etc/logrotate.d/下系统自带的配置文件来研究。
注意事项:
logrotate本身是通过系统定时任务cron来在每天的凌晨3-4点之间某个时间点触发,至于logrotate它自己被触发后,会不会对我们指定的日志文件进行预期的切割处理,还取决于我们对logrotate执行动作的条件约束,更多细节可以关注课程https://edu.51cto.com/sd/3f309, 接受免费试看。
应用举例:
cat /etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily
missingok
rotate 60
compress
notifempty
dateext
sharedscripts
postrotate
if [ -f /var/run/nginx.pid ];then
kill -USR1 `cat /var/run/nginx.pid`
fi
endscript
}
copytruncate方式切割tomcat catalina.out日志
注意事项:
以上是典型的生产环境应用举例,配置层面本身不难,重要的地方在于实际运用后会发现并非一切如预期所愿,需要掌握必要的技巧才能用好。细节部分可以关注课程https://edu.51cto.com/sd/3f309, 接受免费试看。
实际在生产环境运用中,还存在一些高阶技巧需要掌握,甚至说部分地方存在一些坑需要我们明确并避开它们。这里列举一些典型的情况,感兴趣的朋友可以关注课程https://edu.51cto.com/sd/3f309, 接受免费试看,相信您会有不一样的收获。
copytruncate的原理决定了理论上分析,日志丢失不可避免。
日志丢失的程度参考:
https://incoherency.co.uk/blog/stories/logrotate-copytruncate-race-condition.html
I wrote a small C program to test it (outputting increasing numbers as fast as it could) and, indeed, 4 million lines were lost during the course of the log rotation.
要么规避,要么解决。
基于自己对logrotate的理解和生产环境实际运用的一些经历,发现有些看似简单和当然的过程,实际都有非常强的原理和逻辑来支撑。因此萌生了对他们有一个系统的梳理总结的想法,最后想着还是以课程分享输出,捣鼓一阵,在原来的构想基础上,增加了一些概念性和原理性的输出,近期算是上架了。
课程合计无限接近5小时,还是有一定的成本。主要的部分摘取在这里了,章节设计原汁原味,一方面有一定基础且结合文章能领会略去细节的朋友,阅读之后相信依然会有所收获,不妨碍他们对logrotate的进一步理解,另一方面,如若领会这篇文章略有难度,也可以考虑关注一下课程,无限接近5小时课程一份盒饭的价格,从概念到原理,从配置讲解到实战运用,以及生产环境下应用的高阶技巧、避坑指南等,一篮子设计,相信您会有不一样的收获。
课程以logrotate为主线,突出实战应用性,全面深入讲解自动化处理应用日志的方方面面(包括日志切割/日志轮询、日志压缩、日志周期性删除等),传授整套自动化处理日志的知识体系。
课程大纲,有图有真相:
课程链接:https://edu.51cto.com/sd/3f309