AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / server / 问题 / 959320
Accepted
x-yuri
x-yuri
Asked: 2019-03-22 06:14:57 +0800 CST2019-03-22 06:14:57 +0800 CST 2019-03-22 06:14:57 +0800 CST

缺少日志轮换

  • 772

我的一台服务器最近空间不足。所以我开始研究它。日志占用了分区的nginx一半。另外,我注意到一件奇怪的事情。对于很多网站(60%)存在额外的旋转(example.com-access.log.53.gz当rotate 52)。大多数最大的——但不是全部——只有两次旋转:

example.com-access.log
example.com-access.log.53.gz

50% 的原木只有这两个轮换。有时旋转中只有孔(30%):一个文件或更多。*.log.1经常失踪(25%)。*.log.1有时两者都有*.log.1.gz(172 个中有 2 个)。

你能解释一下这个缺失/重复的旋转吗?*.log+ *.log.53.gzcase 让我觉得在某些时候它无法旋转*.log.1到*.log.2.gz. 但是不成功后不会停止gzip吗?那么一定没有孔。或者至少必须*.log.1存在,如果它不存在,不是吗?

如果有的话,我正在运行 Debian 服务器。

/etc/logrotate.conf:

# 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:

/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不存在。

linux
  • 1 1 个回答
  • 1320 Views

1 个回答

  • Voted
  1. Best Answer
    x-yuri
    2019-03-22T16:03:36+08:002019-03-22T16:03:36+08:00

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

    这是幕后发生的事情的简化版本(日志集是配置中的一个条目/path/to/dir/*.log {...}):

    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也不会破坏循环。

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

    有了这个,我可以看到可能丢失或额外文件的唯一情况是何时logrotate被中断。这可能解释 rotate + 1了旋转(gzipped 旋转已重命名,但最后一个尚未删除)。此外,当gzip 被中断时,它会将目标文件留在后面。这解释了同时具有 *.log.1和*.log.1.gz旋转。仍然没有解释旋转中的孔。

    UPD出现重复(*.log.1+ *.log.1.gz)会产生 错误 :

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

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

    这解释了除了缺少*.log.1旋转之外的一切。但可能和它一样好。

    因此,请注意日志轮换中的错误。logrotate您可以通过在(甚至是非详细)输出中发现“错误:”行来识别问题。


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

    #!/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
    

    还有一个让你检查你是否遇到了我遇到的问题:

    #!/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

相关问题

  • Linux 主机到主机迁移

  • 如何在 Linux 机器上找到有关硬件的详细信息?

  • 如何在 Linux 下监控每个进程的网络 I/O 使用情况?

  • 在 RHEL4 上修改 CUPS 中的现有打印机设置

  • 为本地网络中的名称解析添加自定义 dns 条目

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    新安装后 postgres 的默认超级用户用户名/密码是什么?

    • 5 个回答
  • Marko Smith

    SFTP 使用什么端口?

    • 6 个回答
  • Marko Smith

    命令行列出 Windows Active Directory 组中的用户?

    • 9 个回答
  • Marko Smith

    什么是 Pem 文件,它与其他 OpenSSL 生成的密钥文件格式有何不同?

    • 3 个回答
  • Marko Smith

    如何确定bash变量是否为空?

    • 15 个回答
  • Martin Hope
    Tom Feiner 如何按大小对 du -h 输出进行排序 2009-02-26 05:42:42 +0800 CST
  • Martin Hope
    Noah Goodrich 什么是 Pem 文件,它与其他 OpenSSL 生成的密钥文件格式有何不同? 2009-05-19 18:24:42 +0800 CST
  • Martin Hope
    Brent 如何确定bash变量是否为空? 2009-05-13 09:54:48 +0800 CST
  • Martin Hope
    cletus 您如何找到在 Windows 中打开文件的进程? 2009-05-01 16:47:16 +0800 CST

热门标签

linux nginx windows networking ubuntu domain-name-system amazon-web-services active-directory apache-2.4 ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve