案例十七:归档老日志
在Linux系统中有日志切割的工具logrotate,它可以按照我们的预期按时间或者按大小来切割和归档老的日志,它还可以压缩切割后的日志,也可以定义老日志保留的时间。本案例的需求是通过一个shell脚本来实现类似logrotate的功能,具体要求如下:
1)要处理的日志路径/data/logs/1.log
2)每天0点0分切割日志
3)老日志保留一周
4)归档后的日志名字为1.log.1, 1.log.2, ..., 1.log.7
5)假设日志归档后,新日志可以自动生成
知识点一:logrotate
首先声明,本案例脚本并不会引用该部分知识点,在这里仅做简单介绍,下面通过一段配置来解释其含义。
/var/log/messages { //日志的路径
rotate 5 //保留5份归档后的日志
weekly //一周切割一次
postrotate //定义切割日之后,要执行的指令
/usr/bin/killall -HUP syslogd //重载syslogd服务,这样会生成新的/var/log/messages文件
endscript //表示执行shell命令到此结束
}
"/var/log/httpd/access.log" /var/log/httpd/error.log { //同时写多个日志的路径
rotate 5
mail [email protected] //把归档后的日志发动给该邮箱地址
size 100k //当日志文件达到100k时开始做切割
sharedscripts //对于第一行定义的所有日志来说,以下的shell命令(/usr/bin/killall -HUP httpd)只运行一次,而不是每切割一个日志就执行一次。
postrotate
/usr/bin/killall -HUP httpd
endscript
}
/var/log/news/* { //支持*通配
monthly //一个月切割一次日志
rotate 2 //保留两个归档日志
olddir /var/log/news/old //将切割后的日志保存到这个目录下面
missingok //如果日志文件丢失,不报错
postrotate
kill -HUP ‘cat /var/run/inn.pid‘
endscript
nocompress //归档后的日志不压缩,与其相对的参数是compress
}
知识点二:shell中的exit、break和continue
1)exit
在shell脚本中使用exit,则直接退出当前脚本,exit后续的所有指令都不再执行,示例脚本exit.sh内容如下:
#!/bin/bash
for i in `seq 1 5`
do
echo $i
if [ $i -eq 3 ]
then
exit 1
fi
echo $i
done
echo aaaaa
执行该脚本结果如下:
# sh exit.sh
1
1
2
2
3
exit后面可以跟一个数字,表示该脚本的返回值,执行完脚本后,执行echo $?可以打印脚本的返回值。
# echo $?
1
2)break
在shell脚本的循环中,可以使用break退出循环体,示例脚本break.sh如下:
#!/bin/bash
for i in `seq 1 5`
do
echo $i
if [ $i -eq 3 ]
then
break
fi
echo $i
done
echo aaaaa
执行该脚本,结果如下:
# sh break.sh
1
1
2
2
3
aaaaa
说明:当$i为3时,遇到break指令,此时会跳出整个循环,从而执行echo aaaaa指令。
3)continue
和break类似,continue作用是结束本次循环,继续下一次循环,而不会结束整个循环体,示例脚本continue.sh如下:
#!/bin/bash
for i in `seq 1 5`
do
echo $i
if [ $i -eq 3 ]
then
continue
fi
echo $i
done
echo aaaaa
执行脚本,结果如下:
# sh continue.sh
1
1
2
2
3
4
4
5
5
aaaaa
说明:当$i=3时,遇到continue指令,此时结束本次循环,即continue后面的指令echo $i不再执行,继续后续循环,即i=4。
本案例参考脚本
#!/bin/bash
#日志切割归档,按天切割,1.log变1.log.1, 1.log.1变1.log.2, ...
#作者:阿铭
#日期:2018-11-1
logdir=/data/logs/
#定义函数,如果一个文件存在,则删除
function e_df()
{
if [ -f $1 ]
then
rm -f $1
fi
}
cd $logdir
#从7到2,依次遍历循环
for i in `seq 7 -1 2`
do
#$i2比$i小1
i2=$[$i-1]
#首先判断1.log.7是否存在,若存在则删除
e_df 1.log.$i
#当1.log.6存在,则把1.log.6改名为1.log.7,依次类推
if [ -f 1.log.$i2 ]
then
mv 1.log.$i2 1.log.$i
fi
done
#由于1.log后面无后缀,所以不能走上面的for循环,只能另外拿出来处理
e_df 1.log.1
mv 1.log 1.log.1
##说明:这个脚本写完后,放到计划任务里,每天0点0分执行。
需求扩展
现在的需求是,日志按大小来切割,比如当日志大于等于100M时,则需要处理,处理后的日志需要压缩,并且延迟一天,即1.log.1不用压缩,其他需要压缩。
#!/bin/bash
#日志切割归档,按日志大小切割(100M),1.log变1.log.1, 1.log.1变1.log.2, ...
#作者:阿铭
#日期:2018-11-1
logdir=/data/logs/
#技术1.log大小
size=`du -sk $logdir/1.log |awk '{print $1}`
#如果1.log小于100M,则退出脚本
if [ $size -lt 10240 ]
then
exit 0
fi
#定义函数,如果一个文件存在,则删除
function e_df()
{
if [ -f $1 ]
then
rm -f $1
fi
}
cd $logdir
#如果1.log.1存在,则先把它压缩为1.log.1.gz,这样下面的for循环才不会出错
if [ -f 1.log.1 ]
then
gzip 1.log.1
fi
#由于1.log.1已经被压缩为1.log.gz,所以可以直接将1.log改名为1.log.1
mv 1.log 1.log.1
#从7到2,倒序循环
for i in `seq 7 -1 2`
do
#$i2比$i小1
i2=$[$i-1]
#首先判断1.log.7.gz是否存在,若存在则删除
e_df 1.log.$i.gz
#当1.log.6.gz存在,则把1.log.6.gz改名为1.log.7.gz,依次类推
if [ -f 1.log.$i2.gz ]
then
mv 1.log.$i2.gz 1.log.$i.gz
fi
done
##说明:由于我们需要按照日志大小切割,所以这个脚本写完后,需要每分钟执行