问题
新服务器 (CentOS 6.8), 按照之前服务器的 logrotate 配置, 隔天发现日志文件并没有被切分.
解决思路
首先怀疑配置文件错误.
(其实应该先执行 logrotate -d /path/to/your/log
检查配置是否正确, 再看日志, 再不行再去找其他配置. 这里为了梳理下知识, 先过一遍配置文件).
配置文件
沿着 logrotate 的执行顺序一个一个撸:
-
cron
命令每日会执行/etc/cron.daily/
目录下的脚本, 即/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
exit 0
You have mail in /var/spool/mail/root
- 即 logrotate 会读取
/etc/logrotate.conf
文件. 其中有一行配置:
include /etc/logrotate.d
- 即 logrotate 会日常执行
/etc/logrotate.d
目录下的配置:
$ ls /etc/logrotate.d/
my_project_logs dracut iscsiuiolog monit syslog yum
这么一路看下来, 各个配置都没错. 只能开始撸日志.
日志文件
- 首先查看
cron
日志, 即/var/log/cron
, 发现 logrotate 被正常执行了:
Dec 20 03:44:01 web3 anacron[17495]: Job `cron.daily' started
Dec 20 03:44:01 web3 run-parts(/etc/cron.daily)[20606]: starting logrotate
Dec 20 03:44:01 web3 run-parts(/etc/cron.daily)[20622]: finished logrotate
Dec 20 03:44:01 web3 run-parts(/etc/cron.daily)[20606]: starting makewhatis.cron
Dec 20 03:44:01 web3 run-parts(/etc/cron.daily)[20767]: finished makewhatis.cron
Dec 20 03:44:01 web3 run-parts(/etc/cron.daily)[20606]: starting mlocate.cron
Dec 20 03:44:01 web3 run-parts(/etc/cron.daily)[20778]: finished mlocate.cron
Dec 20 03:44:01 web3 anacron[17495]: Job `cron.daily' terminated
Dec 20 03:44:01 web3 anacron[17495]: Normal exit (1 job run)
- 然后查看系统日志
/var/log/messages
, 发现并无 logrotate 的出错信息.
之所以要看系统日志, 是因为/etc/cron.daily/logrotate
这个脚本中写的很明确, 如果 logrotate 执行后返回值不为0, 则打印一条信息到系统日志. 详见上面贴出的/etc/cron.daily/logrotate
脚本内容. - 然后查看 logrotate 的历史记录文件
/var/lib/logrotate.status
(截取一部分贴出来):
$ cat /var/lib/logrotate.status
logrotate state -- version 2
"/var/log/yum.log" 2017-11-10
"/cloud/logs/my_project.log" 2017-12-20
...
问题的根本原因就是这个 /var/lib/logrotate.status
文件.
logrotate.status
/var/lib/logrotate.status
这个文件用以记录日志文件的最后处理日期. 每次执行 logrotate, 其都会检查这个文件, 如果发现配置周期内已经执行了切分, 就不会再次切分.
比如配置文件写了 weekly, 且昨天刚刚切分了文件, 则这次切分会被 logrotate 记录进文件 /var/lib/logrotate.status
. 在此后一周内, 无论手动执行 logrotate 命令, 还是通过 cron 执行, 都不会再次执行切分动作. (如果使用 -v
参数打印执行详情, 会发现要处理的日志文件下有 log does not need rotating
这句)
如果指定 -f
(force) 参数执行 logrotate 命令, 则会强制再次执行切分.
当然以上都是配置了日志按时间周期切分的情况, 按文件大小切分的配置不受此影响.
因此, 在第一次为某日志文件 (假设为 /cloud/logs/my_project.log) 配置了 logrotate 时, logrotate 的内心是崩溃的. 因为 /var/lib/logrotate.status
这个文件里没有 /cloud/logs/my_project.log 这个文件的记录, 所以 logrotate 不知道是否要切分该文件. 于是在第一天时, logrotate 仅把 /cloud/logs/my_project.log 这个文件记录在案, 其他啥也没做.
问题解决
解决办法就是: 啥也不用做… 第二天一切就都正常了.
如果一定要切分当天的日志, 那就用 -f
参数手动执行 logrotate 来实现吧.
附
logrotate 重要配置:
compress --> 压缩日志文件的所有非当前版本
daily,weekly,monthly --> 按指定计划轮换日志文件
delaycompress --> 压缩所有版本,除了当前和下一个最近的
endscript --> 标记 prerotate 或 postrotate 脚本的结束
errors "emailid" --> 给指定邮箱发送错误通知
missingok --> 如果日志文件丢失,不要显示错误
notifempty --> 如果日志文件为空,则不轮换日志文件
olddir "dir" --> 指定日志文件的旧版本放在 “dir” 中
postrotate --> 引入一个在日志被轮换后执行的脚本
prerotate --> 引入一个在日志被轮换前执行的脚本
rotate 'n' --> 在轮换方案中包含日志的 n 个版本
sharedscripts --> 对于整个日志组只运行一次脚本
size='logsize' --> 在日志大小大于 logsize(例如 100K,4M)时轮换
文章目的
几年前刚刚接触服务器运维时, 接到任务, 让按天拆分 nginx 日志. 当时也没多想, 直接就写了个 shell 脚本, 用 crontab 每日执行解决问题. 后来才知道有 logrotate 这个神器, 随即将全部服务器 log 都改为由其管理. 但当时未留下笔记.
今天遇到同事碰到该问题, 正好一并将笔记补上, 以便日后查阅.
附很久前写的日志拆分脚本:
#!/bin/bash
# AUTHOR : liuxu
# date : 2015-10-14
#
# 1./cloud/logs/*.log 移到 /cloud/logs/backlog/*.log.YYYYMMDD 同时生成原来相同文件名的文件
# 2.每天零点执行shell脚本
# 3.重启/usr/local/nginx/sbin/nginx -s reload
SOURCE_DIR=/cloud/logs
TARGET_DIR=
SUFFIX=$(date -d yesterday +%Y%m%d)
NGINX=/usr/local/nginx/sbin/nginx
B_VALIDATE=false
TIME=$(date +"%Y-%m-%d %H:%M:%S")
MV_HISTORY=
#====================================
DEBUG=false
DEBUG() {
if $DEBUG; then
$@
fi
}
function ShellHelp() {
cat <<EOF
--------------------------------------------------------------------------------
USAGE:
nginx_log_splitter.sh [-s source_dir] [-t target_dir] [-n nginx_cmd]
OPTIONS:
-h: print help
-v: ("v" for validate) validate environment, will not move logs, will not reload nginx
-s: ("s" for source) log dir
-t: ("t" for target) target dir. default is $SOURCE_DIR/backlog/
-n: ("n" for nginx) specify nginx command path. default value is $NGINX
--------------------------------------------------------------------------------
EOF
}
#process options
function ProcessOptions() {
while getopts ":hvs:t:n:" opt; do
DEBUG echo "opt: $opt"
case "$opt" in
"h")
ShellHelp
exit 0
;;
"v")
B_VALIDATE=true
;;
"s")
SOURCE_DIR=$OPTARG
;;
"t")
TARGET_DIR=$OPTARG
;;
"n")
NGINX=$OPTARG
;;
"?")
#Unknown option
echo "* unknown option: $opt"
;;
":")
#an option needs a value, which, however, is not presented
echo "* option -$opt needs a value, but it is not presented"
;;
*)
#unknown error, should not occur
echo "* unknown error while processing options and params"
;;
esac
done
return $OPTIND
}
function Log() {
if $B_VALIDATE; then
echo $1
else
echo $1 | tee -a $MV_HISTORY
fi
}
#====================================
DEBUG echo "[$TIME] script begin"
ProcessOptions "$@"
if [ ! -d "$SOURCE_DIR" ]; then
echo "[$TIME] source dir does not exist: $SOURCE_DIR"
exit 1
fi
if [ "$TARGET_DIR" == "" ]; then
TARGET_DIR=$SOURCE_DIR/backlog
fi
DEBUG echo "[$TIME] target dir: $TARGET_DIR"
if [ ! -d "$TARGET_DIR" ]; then
mkdir $TARGET_DIR
if [ $? != 0 ]; then
echo "[$TIME] target dir create fail: $TARGET_DIR"
exit 2
fi
fi
MV_HISTORY=$TARGET_DIR/mv_history
if [ ! -e "$NGINX" ]; then
Log "[$TIME] nginx cmd file does not exist: $NGINX"
exit 3
fi
cd $SOURCE_DIR
log_arr=$(ls *.log 2>/dev/null)
if [ $? -ne 0 ]; then
Log "[$TIME] no log file found under $SOURCE_DIR"
exit 4
fi
log_count=${#log_arr[*]}
if [ $log_count -eq 0 ]; then
Log "[$TIME] no log file found under $SOURCE_DIR"
exit 4
fi
if $B_VALIDATE; then
echo "[$TIME] vadilation success"
exit 0
fi
for f in ${log_arr[*]}; do
mv $SOURCE_DIR/$f $TARGET_DIR/$f.$SUFFIX
ret=$?
if [ $ret == 0 ]; then
echo "[$TIME] mv $SOURCE_DIR/$f to $TARGET_DIR/$f.$SUFFIX" | tee -a $MV_HISTORY
touch $SOURCE_DIR/$f
else
echo "[$TIME] mv $SOURCE_DIR/$f to $TARGET_DIR/$f.$SUFFIX fail ($ret)" | tee -a $MV_HISTORY
fi
done
$NGINX -s reload