什么是 logrotate

logrotate 是一款用来切割日志的工具,更确切的说,是切割文本的工具,但通常用在软件日志切割上。为什么要进行日志切割呢?原因可以有很多,最明显的一个就是防止日志文件变得太大。

lorotate的切割方式

以 Nginx 为例,假设其错误日志放在 /data/proclog/log/nginx/ 下,名为 nginx_error.log,当 logrotate 运行时,如果满足切割要求了,则会将 nginx_error.log 改名为 nginx_error.log.1,并重新创建一个新的空文件 nginx_error.log 作为新的错误日志。

当进行其二次切割时,nginx_error.log.1 被改名为 nginx_error.log.2,刚才创建的 nginx_error.log 被改名为新的 nginx_error.log.1,然后再次重新创建一个新的空文件 nginx_error.log 作为新的错误日志投入使用。

当进行第三次切割时,nginx_error.log.2 变为 nginx_error.log.3,nginx_error.log.1 变为nginx_error.log.2,nginx_error.log 变为 nginx_error.log.1,一个新的 nginx_error.log 被再次创建。

依次类推。

至于到底会保留多少份 nginx_error.log.N(N 代表数字),则是在 logrotate 的配置文件一个参数 rotate 设置的。例如,当 rotate 设置为3时,则只会保留 nginx_error.log.1、nginx_error.log.2 和 nginx_error.log.3,老的文件会被删除。

logrotate 与 crond

logrotate 默认会放在 cron.daily 目录下,每天自动运行。 执行:

cat /etc/cron.daily/logrotate  

看到该文件内容如下:

#!/bin/sh  
  
/usr/sbin/logrotate /etc/logrotate.conf  
EXITVALUE=$?  
if [ $EXITVALUE != 0 ]; then  
    /usr/bin/logger -t logrotate "ALERT exited abnormally with [$EXITVALUE]"  
fi  

上面的 shell 脚本主要是执行 /usr/sbin/logrotate 程序,并检查执行状态,如果执行出错,则通过 logger 程序向系统日志中发送一条错误消息。

注意,现在说道的 logrotate 有两个,一个是 shell 脚本,位于 /etc/cron.daily 下,一个是可执行文件,位于 /usr/sbin 目录下。

继续看上面的 logrotate 脚本,其中错误处理语句中,logger 程序是怎么运行的呢?我们可以做个小测试。 执行:

/usr/bin/logger -t strider "Good Morning"  

屏幕没有任何打印信息,但我们可以查看 /var/log/message:

tail /var/log/messages  

观察最后一条信息:

Sep 10 06:53:55 localhost strider: Good Morning  

可见 logrotate 脚本的错误信息通过通过 logger 程序送到系统日志中去了。

/etc/cron.daily、/etc/crontab

上面我们说到,logrotate 脚本放在 /etc/cron.daily 目录下,所以它会每天定期执行一次。为什么放在 cron.daily 目录下就会有此效果呢?而且,到底是在每天的什么时候执行呢?

linux 下的定时任务是由系统服务 crond 来管理的。crond 的配置文件是 /etc/crontab,可以打开观察一下:

cat /etc/crontab  

会看到如下内容:

SHELL=/bin/bash  
PATH=/sbin:/bin:/usr/sbin:/usr/bin  
MAILTO=root  
HOME=/  
  
### run-parts  
01 * * * * root run-parts /etc/cron.hourly  
02 4 * * * root run-parts /etc/cron.daily  
22 4 * * 0 root run-parts /etc/cron.weekly  
42 4 1 * * root run-parts /etc/cron.monthly  

其中,SHELL 字段、PATH 字段等内容不用多说,重点看下下面的字段。如果你用 crontab -e 指令来写过 crontab 定时任务的话,一定会觉得跟 crontab 中的格式一模一样。其实这是错觉,/etc/crontab 的配置中,每一行都多了一个 root,表示以什么用户来执行,而 crontab -e 配置中,则没有这一个用户名字段。

虽然有这一点区别,其他地方意义还是一样的,例如:

02 4 * * * root run-parts /etc/cron.daily  

就表明,每天凌晨的 4:02 时刻,以 root 身份执行 run-parts /etc/cron.daily 命令。我们大概也能猜出来了,run-parts 的作用就是执行后面目录下的所有脚本,所以放在 cron.daily 目录下的脚本,会每天被执行一次。cron.hourly、cron.weekly、cron.monthly 等也是类似。

run-parts

那么 run-parts 到底是怎么做的呢?我们可以确定一下,执行:

whereis run-parts  

输出:

run-parts: /usr/bin/run-parts  

查看 run-parts 类型,执行:

file /usr/bin/run-parts  

输出:

/usr/bin/run-parts: Bourne-Again shell script text executable  

可见 run-parts 是一个 bash 脚本,其内容及注释如下:

#!/bin/bash  
  
# run-parts - concept taken from Debian  
  
# keep going when something fails  
#当shell中某些命令或子shell执行错误时,该脚本让然能够执行下去。  
set +e  
  
#如果参数小于1,则打印用法信息,并退出  
if [ $# -lt 1 ]; then  
        echo "Usage: run-parts <dir>"  
        exit 1  
fi  
  
#如果所带参数不是一个目录,则报错,并退出  
if [ ! -d $1 ]; then  
        echo "Not a directory: $1"  
        exit 1  
fi  
  
# Ignore *~ and *, scripts  
#忽略该目录下,文件名中带有~和,的脚本  
for i in $1/*[^~,] ; do  
        #忽略子目录  
        [ -d $i ] && continue  
  
        #忽略rpmsave、rpmorig、rpmnew、swp、v格式的文件  
        # Don't run *.{rpmsave,rpmorig,rpmnew,swp} scripts  
        [ "${i%.rpmsave}" != "${i}" ] && continue  
        [ "${i%.rpmorig}" != "${i}" ] && continue  
        [ "${i%.rpmnew}" != "${i}" ] && continue  
        [ "${i%.swp}" != "${i}" ] && continue  
        [ "${i%,v}" != "${i}" ] && continue  
  
        #如果该文件可执行,则执行之  
        if [ -x $i ]; then  
                $i 2>&1 | awk -v "progname=$i" \  
                              'progname {  
                                   print progname ":\n"  
                                   progname="";  
                               }  
                               { print; }'  
        fi  
done  
  
exit 0  

/etc/crontab 和 crontab -e

与前面所说的 logrotate 一样,也有两个都叫 crontab 的文件,一个是 /etc/crontab,是 crond 的配置文件,另一个是一个程序,是用户与 crond 交互的外部接口。

前面已经提到,/etc/crontab 与 crontab 的配置格式是不一样的,例如:

/etc/crontab中:

...(略)  
02 4 * * * root run-parts /etc/cron.daily  

与程序 crontab 对比一下,执行 crontab -u root -l,可得到:

* * * * * /home/strider/project/worker/fdfs-v4.07/script/fdfs_worker_demeon.sh  
* * * * * /bin/bash /home/strider/project/worker/fdfs-v4.07/script/fdfs_add_task.sh 

可见,通过 crontab 程序设置 crond 时,是不需要添加用户的。原因很简单,crontab 必须在执行时指定用户名(如果不指定,则默认为当前登录的用户),执行 crontab -u username -e 设置完规则后,这些规则会保存在 /var/spool/cron/username 文件中,每个用户的 crontab 配置都会保存在属于该用户的文件中。例如:

cat /var/spool/cron/root  

输出:

* * * * * /home/strider/project/worker/fdfs-v4.07/script/fdfs_worker_demeon.sh  
* * * * * /bin/bash /home/strider/project/worker/fdfs-v4.07/script/fdfs_add_task.sh 

cront -u root -l 回显一致。