具体问题引出的 logrotate 复习笔记

问题

新服务器 (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 </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

你可能感兴趣的:(具体问题引出的 logrotate 复习笔记)