美文网首页Linux运维Linux学习之路我用 Linux
具体问题引出的 logrotate 复习笔记

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

作者: _Lyux | 来源:发表于2017-12-20 15:49 被阅读15次

    问题

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

    相关文章

      网友评论

        本文标题:具体问题引出的 logrotate 复习笔记

        本文链接:https://www.haomeiwen.com/subject/ycugwxtx.html