首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >丢失的日志轮转

丢失的日志轮转
EN

Server Fault用户
提问于 2019-03-21 14:14:57
回答 1查看 1.6K关注 0票数 1

我的一台服务器最近没空间了。所以我开始调查。nginx日志占用了一半的分区。还有,我注意到一件奇怪的事。对于很多网站(60%)额外的旋转存在(example.com-access.log.53.gzrotate 52)。而大多数最大的--但不是所有--只有两次轮值:

代码语言:javascript
复制
example.com-access.log
example.com-access.log.53.gz

50%的原木只有这两次旋转。有时在旋转中只会出现漏洞(30%):一个或多个文件。*.log.1常缺失(25%)。有时出现*.log.1*.log.1.gz (2 / 172)。

你能解释一下这个缺少的/重复的旋转吗?*.log + *.log.53.gz的情况让我认为,在某些时候,它无法将*.log.1旋转到*.log.2.gz。但是,在gzip失败之后,它不会停止吗?那就不可能有洞。或者至少必须有*.log.1在场如果它不会,不是吗?

我在运行Debian服务器。

/etc/logrotate.conf

代码语言:javascript
复制
# see "man logrotate" for details
# rotate log files weekly
weekly

# keep 4 weeks worth of backlogs
rotate 4

# create new (empty) log files after rotating old ones
create

# uncomment this if you want your log files compressed
#compress

# packages drop log rotation information into this directory
include /etc/logrotate.d

# no packages own wtmp, or btmp -- we'll rotate them here
/var/log/wtmp {
    missingok
    monthly
    create 0664 root utmp
    rotate 1
}

/var/log/btmp {
    missingok
    monthly
    create 0660 root utmp
    rotate 1
}

# system-specific logs may be configured here

/etc/logrotate.d/nginx

代码语言:javascript
复制
/var/log/nginx/*.log {
    weekly
    missingok
    rotate 52
    compress
    delaycompress
    notifempty
    create 0640 www-data adm
    size 50M
    sharedscripts
    prerotate
        if [ -d /etc/logrotate.d/httpd-prerotate ]; then \
            run-parts /etc/logrotate.d/httpd-prerotate; \
        fi \
    endscript
    postrotate
        [ -s /run/nginx.pid ] && kill -USR1 `cat /run/nginx.pid`
    endscript
}

/etc/logrotate.d/httpd-prerotate不存在。

EN

回答 1

Server Fault用户

回答已采纳

发布于 2019-03-22 00:03:36

如果压缩(gzip)中断,可能会产生tl;dr重复。一个这样的重复(如果sharedscripts)使它最终只留下一个gzipped旋转(#rotate + 1)。

下面是对在引擎盖下面的简化版本(logset是配置中的条目/path/to/dir/*.log {...} ):

代码语言:javascript
复制
for (let logSet of logSets) {
    rotateLogSet(logSet);
}

function rotateLogSet(logSet) {
    const logHasErrors = [];
    let hasErrors = false;
    for (let i = 0; i < logSet.files().length; i++) {
        findNeedRotating(log, i);
    }
    const jn = logSet['sharedscripts']
        ? 1
        : logSet.files().length;
    for (let j = 0; j < jn; j++) {
        const in = logSet['sharedscripts'] ? logSet.files().length : j + 1;
        for (let i = j; i < in; i++) {
            logHasErrors[i] ||= ! prerotateSingleLog(logSet, i);
            hasErrors ||= logHasErrors[i];
        }
        if (logSet['prerotate']
            && ( ! (
                logSet['sharedscripts'] ? hasErrors : logHasErrors[j]
            ))
        ) {
            if ( ! runScriptMultiple(logSet['prerotate']))
                logHasErrors[j] = hasErrors = true;
        }
        for (let i = j; i < in; i++) {
            if ( ! (
                logSet['sharedscripts'] ? hasErrors : logHasErrors[i]
            ))
                logHasErrors[i] ||= ! rotateSingleLog(logSet, i);
                hasErrors ||= logHasErrors[i];
        }
        if (logSet['postrotate']
            && ( ! (
                logSet['sharedscripts'] ? hasErrors : logHasErrors[j]
            ))
        ) {
            if ( ! runScriptMultiple(logSet['postrotate']))
                logHasErrors[j] = hasErrors = true;
        }
        for (let i = j; i < in; i++) {
            if ( ! (
                logSet['sharedscripts'] ? hasErrors : logHasErrors[i]
            ))
                logHasErrors[i] ||= ! postrotateSingleLog(logSet, i);
                hasErrors ||= logHasErrors[i];
        }
    }
}

function findNeedRotating(logSet, i) {
    const log = logSet.files()[i];
    if ( ! stat(log))
        return logSet['missingok'] && errno == ENOENT;
    log.doRotate = ...;
    return ...;
}

function prerotateSingleLog(logSet, i) {
    let hasErrors = false;
    const log = logSet.files()[i];
    if ( ! log.doRotate)
        return;
    if (stat(log))
        hasErrors = compressLogFile(log);
    for (let i = logSet['rotate']; i >= 0 && ! hasErrors; i--) {
        if ( ! rename(`${log}.${i}.gz`, `${log}.${i + 1}.gz`))
            if (errno != ENOENT)
                hasErrors = true;
    }
    return ! hasErrors;
}

function rotateSingleLog(logSet, i) {
    let hasErrors = false;
    const log = logSet.files()[i];
    if ( ! log.doRotate)
        return;
    if ( ! rename(log, `${log}.1`))
        hasErrors = true;
    if ( ! hasErrors && logSet['create'])
        if ( ! createOutputFile(log))
            hasErrors = true;
    return ! hasErrors;
}

function postrotateSingleLog(logSet, i) {
    const log = logSet.files()[i];
    if ( ! log.doRotate)
        return;
    rm(`${log}.${logSet['rotate'] + 1}.gz`);
}

因此,对于通常属于sharedscripts的错误,在处理属于日志集的日志文件时发生的错误将停止对整个日志集的处理。没有它,只会停止一个日志文件的处理。

但是不存在的gzipped旋转或日志文件的第一次旋转并不算作错误。当日志文件本身不存在时,如果missingok (在模式情况下不重要),情况就不一样了。此外,在prerotateSingleLog()阶段使用sharedscripts时出现的错误不会中断循环。

请注意,我在编译上面的代码时做了很多简化。如有疑问,请查阅原件。

With这个,我能看到的唯一情况是在logrotate被中断的时候可能会丢失或额外的文件。这也许可以解释为什么rotate + 1旋转(gzipped旋转被重命名了,但最后一个旋转还没有被删除)。此外,当gzip被中断时,它会将目标文件抛在后面。这就解释了为什么有*.log.1*.log.1.gz的轮值。仍然没有解释旋转中的洞。

UPD它看起来是重复的(*.log.1 + *.log.1.gz) 生产 一个 错误

错误:创建输出文件/var/log/nginx/example.com- exists .log.1.gz:文件存在

这将在prerotateSingleLog()阶段之后停止处理。此时,所有的gzipped旋转都被重命名了。但是会跳过重命名*.log -> *.log.1和删除*.log.${rotate + 1}.gz*.log变得越来越大,最终你就没有空间了。

这就解释了除了缺少*.log.1轮转之外的所有事情。但可能是最好的了。

因此,要注意日志旋转中的错误。您可以通过在logrotate (甚至是非冗长的)输出中发现"error:“行来识别问题。

作为奖励,一个脚本以一种很好的方式显示日志dir的内容(自然排序,具有大小):

代码语言:javascript
复制
#!/usr/bin/env bash
set -eu
for l in /var/log/nginx/*.log; do
    du -bs "$l"* \
        | sed -E 's/(.*\.([0-9]+)(\.gz)?)$/\2 \1/; t a; s/^/0 /; :a' \
        | sort -nk1 \
        | awk '{sub(/.*\//, "", $3); printf("%12s %s\n", $2, $3)}'
done

另一个让我们来看看你是否有我遇到的问题:

代码语言:javascript
复制
#!/usr/bin/env bash
set -eu

dir=/var/log/nginx

i=0
n_0_53=0
n_53=0
n_holes=0
n_missing_1=0
for f in `ls "$dir"/*.log`; do
    f=`basename -- "$f"`
    echo -- $f
    rotations=$(ls "$dir/$f"* \
        | sed -E 's/(.*\.([0-9]+)(\.gz)?)$/\2 \1/; t a; s/^/0 /; :a' \
        | sort -nk1 \
        | awk '{print $1}')

    duplicates=$(echo "$rotations" | uniq -c | awk '$1 != 1 {print $2}')
    if [ "$duplicates" ]; then
        echo duplicates: $duplicates
    fi

    if [ "$rotations" = 0\n53' ]; then
        echo 0, 53
        (( n_0_53 += 1))
    else
        missing=
        last=$(echo "$rotations" | tail -n 1)
        for (( j = 0; j <= $last; j++ )); do
            if ! [[ "$rotations" =~ (^|\n')"$j"(\n'|$) ]]; then
                missing="$missing $j"
            fi
        done
        if [ "$missing" ]; then
            echo missing: $missing
            (( n_holes += 1 ))
        fi
        if [ "$missing" = ' 1' ]; then
            (( n_missing_1 += 1 ))
        fi
    fi
    if [[ "$rotations" =~ (^|\n')53(\n'|$) ]]; then
        (( n_53 += 1 ))
    fi
    (( i += 1 ))
done
printf 'n_0_53: %s %s\n' "$n_0_53" "$(echo "$n_0_53 * 100 / $i" | bc)"
printf 'n_53: %s %s\n' "$n_53" "$(echo "$n_53* 100  / $i" | bc)"
printf 'n_holes: %s %s\n' "$n_holes" "$(echo "$n_holes * 100 / $i" | bc)"
printf 'n_missing_1: %s %s\n' "$n_missing_1" "$(echo "$n_missing_1 * 100 / $i" | bc)"
printf 'total: %s\n' "$i"
票数 1
EN
页面原文内容由Server Fault提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://serverfault.com/questions/959320

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档