运行机制
logrotate在不少Linux发行版上都是默认安装的。系统会定时运行logrotate,通常是天天一次。系统是这么实现按天执行的。crontab会天天定时执行/etc/cron.daily目录下的脚本,而这个目录下有个文件叫logrotate。在centos上脚本内容是这样的:
/etc/cron.daily/logrotatenode
/usr/sbin/logrotate /etc/logrotate.conf >/dev/null 2>&1 EXITVALUE=$? if [ $EXITVALUE != 0 ]; then /usr/bin/logger -t logrotate "ALERT exited abnormally with [$EXITVALUE]" fi exit 0
能够看到这个脚本主要作的事就是以/etc/logrotate.conf为配置文件执行了logrotate。就是这样实现了天天执行一次logrotate。
由于个人系统执行/etc/cron.daily目录下的脚本不是我想滚动日志的时间,因此我把/etc/cron.daily/logrotate拷了出来,改了一下logrotate配置文件的路径,而后在crontab里加上一条指定时间执行这个脚本的记录,自定义周期滚动日志就大功告成了。这种自定义的方式有两点要注意:
配置文件里必定要配置rotate 文件数目这个参数。若是不配置默认是0个,也就是只容许存在一份日志,刚切分出来的日志会立刻被删除。
执行logrotate命令最好加-f参数,否则有时候配置文件修改的内容不生效。
不少程序的会用到logrotate滚动日志,好比nginx。它们安装后,会在/etc/logrotate.d这个目录下增长本身的logrotate的配置文件。logrotate何时执行/etc/logrotate.d下的配置呢?看到/etc/logrotate.conf里这行,一切就不言而喻了。nginx
include /etc/logrotate.d
原理
logrotate是怎么作到滚动日志时不影响程序正常的日志输出呢?logrotate提供了两种解决方案。
Linux文件操做机制
介绍一下相关的Linux下的文件操做机制。
Linux文件系统里文件和文件名的关系以下图。编程
目录也是文件,文件里存着文件名和对应的inode编号。经过这个inode编号能够查到文件的元数据和文件内容。文件的元数据有引用计数、操做权限、拥有者ID、建立时间、最后修改时间等等。文件件名并不在元数据里而是在目录文件中。所以文件更名、移动,都不会修改文件,而是修改目录文件。
借《UNIX环境高级编程》里的图说一下进程打开文件的机制。centos
进程每新打开一个文件,系统会分配一个新的文件描述符给这个文件。文件描述符对应着一个文件表。表里面存着文件的状态信息(O_APPEND/O_CREAT/O_DIRECT…)、当前文件位置和文件的inode信息。系统会为每一个进程建立独立的文件描述符和文件表,不一样进程是不会共用同一个文件表。正由于如此,不一样进程能够同时用不一样的状态操做同一个文件的不一样位置。文件表中存的是inode信息而不是文件路径,因此文件路径发生改变不会影响文件操做。日志
方案1:createcode
默认方案没有名字,姑且叫它create吧。由于这个方案会建立一个新的日志文件给程序输出日志,并且第二个方案名copytruncate是个配置项,与create配置项是互斥的。
这个方案的思路是重命名原日志文件,建立新的日志文件。详细步骤以下:
重命名程序当前正在输出日志的程序。由于重命名只会修改目录文件的内容,而进程操做文件靠的是inode编号,因此并不影响程序继续输出日志。
建立新的日志文件,文件名和原来日志文件同样。虽然新的日志文件和原来日志文件的名字同样,可是inode编号不同,因此程序输出的日志仍是往原日志文件输出。
经过某些方式通知程序,从新打开日志文件。程序从新打开日志文件,靠的是文件路径而不是inode编号,因此打开的是新的日志文件。
什么方式通知程序我从新打开日志呢,简单粗暴的方法是杀死进程从新打开。不少场景这种做法会影响在线的服务,因而有些程序提供了从新打开日志的接口,好比能够经过信号通知nginx。各类IPC方式均可以,前提是程序自身要支持这个功能。
有个地方值得一提,一个程序可能输出了多个须要滚动的日志文件。每滚动一个就通知程序从新打开全部日志文件不太划得来。有个sharedscripts的参数,让程序把全部日志都重命名了之后,只通知一次。orm
方案2:copytruncate接口
若是程序不支持从新打开日志的功能,又不能粗暴地重启程序,怎么滚动日志呢?copytruncate的方案出场了。
这个方案的思路是把正在输出的日志拷(copy)一份出来,再清空(trucate)原来的日志。详细步骤以下:
拷贝程序当前正在输出的日志文件,保存文件名为滚动结果文件名。这期间程序照常输出日志到原来的文件中,原来的文件名也没有变。
清空程序正在输出的日志文件。清空后程序输出的日志仍是输出到这个日志文件中,由于清空文件只是把文件的内容删除了,文件的inode编号并无发生变化,变化的是元信息中文件内容的信息。
结果上看,旧的日志内容存在滚动的文件里,新的日志输出到空的文件里。实现了日志的滚动。
这个方案有两个有趣的地方。
文件清空并不影响到输出日志的程序的文件表里的文件位置信息,由于各进程的文件表是独立的。那么文件清空后,程序输出的日志应该接着以前日志的偏移位置输出,这个位置以前会被\0填充才对。但实际上logroate清空日志文件后,程序输出的日志都是从文件开始处开始写的。这是怎么作到的?这个问题让我纠结了好久,直到某天灵光一闪,这不是logrotate作的,而是成熟的写日志的方式,都是用O_APPEND的方式写的。若是程序没有用O_APPEND方式打开日志文件,变会出现copytruncate后日志文件前面会被一堆\0填充的状况。
日志在拷贝完到清空文件这段时间内,程序输出的日志没有备份就清空了,这些日志不是丢了吗?是的,copytruncate有丢失部分日志内容的风险。因此能用create的方案就别用copytruncate。因此不少程序提供了通知我更新打开日志文件的功能来支持create方案,或者本身作了日志滚动,不依赖logrotate。进程