具体问题引出的 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 <<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
    原文作者:_Lyux
    原文地址: https://www.jianshu.com/p/514a9715de46
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞