漫谈 logrotate 与 crond
什么是 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
回显一致。