日志定期清理和logrotate

2019-04-29 18:44:14 浏览数 (1)

常见应用服务,都会记录日志,方便问题查询和故障定位。linux系统本身也会有日志输出。

日志管理策略一般是,通过一定的规则,对日志进行定期清理,防止日志随时间增长占满磁盘空间。

1. 日志定期清理

日志的定期清理,最先想到的方法是通过crontab shell的方式。通过对日志后缀表示或mtime进行判断,rm相应的日志,必要时候还需要reload应用。

1.1 crontab显示定时清理

例如,查找7天前的日志并删除(日志保留7天)

代码语言:txt复制
# 清理脚本

find . -name "*.log" -mtime  7 -delete

find . -name "*.log" -mtime  7 -exec rm -rf {} ;

find . -name "*.log" -mtime  7 | xargs rm

# 凌晨定期清理

0 4 * * * find /data/log -name "*.log" -type f -mtime  7 | xargs rm

删除指定时间前的日志

代码语言:txt复制
find . -name "*.log" -type f -newermt '2018-11-01 00:00' | xargs rm

1.2 crontab隐式定时清理

部分应用安装时自带日志清理脚本,但具体清理的逻辑隐层于脚本中。

e.g.

代码语言:txt复制
0 2 * * * sh /data/abc/rm_log.sh > /dev/null 2>&1

通常这样的日志清理方式,通过crontab -l可以找到相应的脚本。

这里有一个小技巧,通过搜索mon、log等关键字,或者通过时间判断,可以快速找到对应的清理脚本。

e.g. 某些业务,通过先mv (kill -USR)达到日志清理的效果

代码语言:txt复制
#!/bin/sh
#Export PATH
PATH=/sbin:/usr/sbin:/usr/local/sbin:/usr/local/bin:/usr/bin:/bin:$PATH
export PATH
#定义各种日志路径
BasePath="$(cd $(dirname $0) && pwd)"
CurDay=`date  %Y%m%d`
YDay=`date "-d -1 days"  %F`
NginxLogPath="/data/log/nginx"
http_ip="10.9.8.7"
#nginxLogClean
if [ -d "${NginxLogPath}" ] && [ -s "/var/run/nginx.pid" ];then
	LogList=(`find ${NginxLogPath}  -maxdepth 1 -name "*.log" -type f -size  0`)
	if [ ! -d "/data/nginx_log" ];then
		mkdir /data/nginx_log
	fi
	BakDir=/data/nginx_log
	Pid=`cat /var/run/nginx.pid`
	cd $BakDir && /bin/rm ./*log*
	sleep 5
	cd $NginxLogPath
	for Log in ${LogList[@]}
	do
	LogName=`basename $Log`
	mv $LogName $BakDir/${LogName}_$YDay
	done
	#/etc/init.d/nginx reload
	kill -USR1 ${Pid}
fi
原理分析

nginx日志输出过程时,寻找文件是通过inode进行定位文件block,mv操作后,由于inode不变,日志依然正常打印,不会引起nginx hang。kill -USR1操作,根据官方文档解析,是Reopen the log files。因此,nginx接收到信号量后,重新读取配置文件并向配置中的日志路径打印日志,注意日志文件是按绝对/相对路径进行寻址,从而达到了重新写一份新日志的效果。

这样的日志清理逻辑,既复杂又难以管理。

2. logrotate工具

logrotate是linux自带的日志清理工具,linux系统日志的定期清理就是依赖logrotate完成。

2.1 logrotate快速配置

linux使用的logrotate配置存放于/etc/logrotate.conf,其他额外的配置存放于/etc/logrotate.d,可以参考里面的配置文件创建新的清理任务

例如通过yum安装nginx,会生成nginx相关logrotate配置

代码语言:txt复制
[root@zero_machine /etc/logrotate.d]# cat /etc/logrotate.d/nginx 
/var/log/nginx/*log {
    create 0644 nginx nginx
    daily
    rotate 10
    missingok
    notifempty
    compress
    sharedscripts
    postrotate
        /bin/kill -USR1 `cat /run/nginx.pid 2>/dev/null` 2>/dev/null || true
    endscript
}

其中,

create是指定rotation后新log file的相关属性,其他属性将忽略,和original log file相同

daily表示每天执行

rotate 10表示log file循环保留多少份

missingok表示如果日志文件不存在,不报错跳过

notifempty表示日志如果为空,不执行rotate

compress表示老日志使用gzip压缩保存

sharedscripts表示对多个日志,例如//var/log/nginx/*log,postrotate和endscript的script只执行1次,否则对多个日志调用多次

postrotate/endscript表示,在其中的script,是在日志文件rotate后才执行,同理还有prerotate/endscript

其他可查看官方文档

2.2 工作原理

参考这里

简单来说,logrotate有两种模式,create和copytruncate

create模式:

  1. logrotate重命名正在写入的日志文件的文件名,由于读写文件使用文件inode,所以重命名不影响日志继续写入。
  2. 重建新的日志文件,文件名和原来日志文件一样。原程序使用inode继续往旧日志文件写日志。
  3. 程序relaod,触发程序获取新日志inode,新日志写入到新的日志文件中。

这种方式需要依赖程序有reload功能,一般kill -HUP pid能触发程序relaod,nginx还可以使用kill -USR1 pid单独触发日志relaod。

copytruncate模式:

  1. 对老日志文件进行拷贝,生成日志的备份;
  2. 程序清空日志文件,例如echo > logfile
  3. 程序往清空后的日志继续写日志,由于程序使用O_APPEND方式打开日志文件,日志便能重第一行开始重新写入。

这种方式会存在日志丢失的风险。旧日志拷贝后到新日志写入空文件,期间的日志内容便丢失了。

2.3 按大小进行日志循环

按大小rotate日志,即当日志大小到达某个size,日志开始循环。配合rotate N决定日志共保留N * size 大小。

e.g.

代码语言:txt复制
# 保留5个100k的历史日志
"/var/log/httpd/access.log" /var/log/httpd/error.log {
    rotate 5
    mail www@my.org
    size 100k
    sharedscripts
    postrotate
        /usr/bin/killall -HUP httpd
    endscript
}

2.4 按时间进行日志循环

按时间rotate日志,即当时间到达某个点时,便执行日志循环。例如使用hourrotate 5,即保留最近6小时的日志。

e.g.

代码语言:txt复制
# 保留5小时的历史日志
/data/log/nginx/*log {
    hourly
    rotate 5
    missingok
    notifempty
    nocompress
    sharedscripts
    postrotate
        [ -f /data/log/nginx/nginx.pid ] && kill -USR1 `cat /data/log/nginx/nginx.pid`
    endscript
}

2.5 按时间进行日志循环,同时考虑日志大小

如果size和time interval同时使用,系统会忽略size;可以根据不同场景考虑使用minszie和maxsize

If size and time interval options are specified at same time, only size option take effect. it causes log files to be rotated without regard for the last rotation time.

minsize & timeperiod: minsize表明,当到达timeperiod,例如daily,日志大小至少为#MB才会触发rotate,否则啥也不处理。

maxsize & timeperiod: maxsize表明,当日志大小超过maxsize,或者到达timeperiod,满足任一条件,均会触发rotate。


内部原理

执行logrotate,实际需要关注3个条件。

  1. crontab任务频率
  2. logrotate配置文件的timeperiod
  3. logrotate配置文件的minsize/maxsize(size不能和timeperiod同时生效,这时size优先)

logrotate 在每一次运行时,会将它将要做 rotate 的文件时间,写入 /var/lib/logrotate.status (默认位置)文件中,如:

代码语言:txt复制
[root@zero /var/lib]# cat logrotate.status 
logrotate state -- version 2
"/var/log/btmp" 2019-4-1-3:43:1
"/var/log/conman/*" 2018-8-3-15:0:0
....

每次触发logrotate时,程序检查logrotate.status,判断是否进行rotate。


以maxsize & timeperiod为例:

代码语言:txt复制
# crontab
0 4 * * * /usr/sbin/logrotate /tmp/tmplog_rotate

# /tmp/tmplog_rotate
/data/test/tmp.log {
create                 
monthly                         
maxsize 10M                      
rotate 1                        
}

logrotate每天凌晨4点执行,例如4月1号执行,便记录tmplog_rotate日志信息。

使用-d进行debug,由于日志timeperiod没到1个月,size没到10240

代码语言:txt复制
[root@zero_op /data/test]# logrotate -d /tmp/tmplog_rotate 
reading config file /tmp/tmplog_rotate

Handling 1 logs

rotating pattern: /data/test/tmp.log  monthly (1 rotations)
empty log files are rotated, log files >= 10240 are rotated earlier, old logs are removed
considering log /data/test/tmp.log
  log does not need rotating

模拟凌晨运行logrotate,logrotate.status记录log文件信息,不会变化;再次执行logrotate,由于timeperiod和maxsize条件均满足,status信息依然没有变化

代码语言:txt复制
[root@zero_op /data/test]# logrotate /tmp/tmplog_rotate 
[root@zero_op /data/test]# cat /var/lib/logrotate.status  |grep tmp.log
"/data/test/tmp.log" 2019-4-29-15:0:0

使tmp.log大于10k,重试logrotate,此时日志>maxsize,会触发rotate

代码语言:txt复制
[root@zero_op /data/test]# logrotate /tmp/tmplog_rotate 
[root@zero_op /data/test]# cat /var/lib/logrotate.status  |grep tmp.log
"/data/test/tmp.log" 2019-4-29-15:21:43
[root@zero_op /data/test]# ll
total 10240
-rw-r--r-- 1 root root        0 Apr 29 15:21 tmp.log
-rw-r--r-- 1 root root 10485760 Apr 29 15:17 tmp.log.1

结论:当cron频率大于timeperiod,直到timeperiod才会rotate;但期间如果log size > maxszie,也会触发rotate


minsize & timeperiod 例子

e.g.

代码语言:txt复制
/var/log/wtmp {                 //仅针对 /var/log/wtmp 所设定的参数
monthly                         //每月一次切割,取代默认的一周
minsize 1M                      //文件大小超过 1M 后才会切割
create 0664 root utmp           //指定新建的日志文件权限以及所属用户和组
rotate 1                        //只保留一个日志.
}
# 这个 wtmp 可记录用户登录系统及系统重启的时间
# 因为有 minsize 的参数,因此不见得每个月一定会执行一次喔.要看文件大小。

详细解析可参考这里

With logrotate <= v3.8.0 the three supported scenarios are:size rotate by size, once daily at most, regardless of elapsed timeperiod timeperiod rotate unconditionally by timeperiod, regardless of size minsize & timeperiod if logfile size exceed minsize, then rotate by timeperiod. A common use is "minsize 1" which means 0-byte logs don't get rotated, minimising clutter.

logrotate-3.8.1 adds:maxsize & timeperiod rotate when either size exceeds maxsize, or after elapsed timeperiod. logrotate may need to be run more than the default once per day in this case.

logrotate-3.8.5 adds:hourly support, and stores a complete timestamp in the state file. You should run logrotate (at least) hourly for this.

2.6 特别注意

  • 当使用logrotate进行日志切割循环时,应先配置logrotate配置并手工运行尝试。
  • 尽量不要使用59 23 * * * /usr/sbin/logrotate xxxrotate,避免daily执行logrotate时,status记录信息时跨日了,导致少了1天的日志文件。
代码语言:txt复制
access.log-20180101.gz
access.log-20180102.gz
access.log-20180104.gz   # 0103 那天 23:59,没来得及执行 logrotate,导致备份这里的日期是4号的

这时候,4 号 23:59 分执行的 logratote,就不会对 access.log 做 ratation 了。

2.7 参数说明

  • -f:强制执行循环
  • -d:debug模式,不进行实际操作,只打出循环具体日志。
  • -v:会实际操作,打出循环具体日志。

Q && A

Q: rotate 4,日志实际保留多少份?

A: 历史日志4份,log.1,log.2,log.3,log.4, 当前日志1份,共5份日志存在。

Q: -USR1-HUP有什么不同

A: -HUP进程hangup的信号量,比较通用;而-USR1为自定义信号了,具体按软件怎样定义,例如nginx的-USR1是日志reopen的信号量,参考这里

例如IBM的HTTPServer,参考这里

/bin/kill -HUP cat /IBM/HTTPServer/logs/httpd.pid 2> /dev/null || true - Reload/HUP: Sending the HUP or restart signal to the parent causes it to kill off its children like in TERM, but the parent doesn't exit. It re-reads its configuration files, and re-opens any log files. Then it spawns a new set of children and continues serving hits./bin/kill -USR1 cat /IBM/HTTPServer/logs/httpd.pid 2> /dev/null || true - Graceful/USR1: The USR1 or graceful signal causes the parent process to advise the children to exit after their current request (or to exit immediately if they're not serving anything). The parent re-reads its configuration files and re-opens its log files. As each child dies off the parent replaces it with a child from the new generation of the configuration, which begins serving new requests immediately.

参考

https://linux.cn/article-4126-1.html

https://www.thegeekstuff.com/2010/07/logrotate-examples/

https://www.cnblogs.com/kevingrace/p/6307298.html

0 人点赞