客观的

客观的

客观的

因此,我编写了一个脚本,用于记录日志并在日志达到一定大小时轮换日志。我希望捕获其他命令的所有错误和输出,而不是我故意回显到日志中的错误和输出。

我实现这一目标的方法是将 STDIN 和 STDOUT 重定向到tee进程替换中,并编写了一个用于savelog轮换日志的小函数。

#!/bin/bash

LOGFILE="/var/log/ps.log"
exec > >(tee "$LOGFILE") 2>&1

LOGPATH="/var/log"
MAX_LOGFILE_SIZE=5
rotateLog() {
        currentsize=$(du -k $LOGFILE | cut -f1)
        if [ $currentsize -ge $MAX_LOGFILE_SIZE ]; then
                savelog -dn $LOGFILE &>/dev/null
        fi
}

while :; do
    echo "A computer program can easily produce gibberish - especially if it has been provided with garbage beforehand. This program does something a little different. It takes a block of text as input and works out the proportion of characters within the text according to a chosen order. For example, an order of 2 means the program looks at pairs of letters, an order of 3 means triplets of letters and so on. The software can regurgitate random text that is controlled by the proportion of characters. The results can be quite surprising."

    rotateLog

    sleep 5
done

问题

问题是,即使日志ps.log旋转到后ps.log.20180829131658,所有日志仍会写入ps.log.20180829131658并引发以下错误:

du: 无法访问 '/var/log/ps.log': 没有这样的文件或目录 ./ps.sh: 第 12 行: [: -ge: 需要一元运算符

导致日志不再进一步旋转!

假设

我假设一旦ps.log旋转到,就会创建ps.log.20180829131658一个新的日志文件。ps.log但情况绝对不是这样,因为exec只执行一次(谁能解释一下这里到底发生了什么?) 即在脚本的开头。

观察

>(tee "$LOGFILE")我还观察到,为该点创建了一个新的文件描述符,/var/log/p.log并将其重新分配给p.log.20180829131658!这显然导致日志仍然被写入p.log.20180829131658谁能解释一下这种行为吗?

root@b537ccc2c1ab:/var/log# ls -lrt
-rw-r--r-- 1 root root   7248 Aug 29 13:16 ps.log

root@b537ccc2c1ab:/var/log# ls -lrt /proc/*/fd
/proc/8979/fd:
total 0
l-wx------ 1 root root 64 Aug 29 13:16 3 -> /var/log/ps.log
lrwx------ 1 root root 64 Aug 29 13:16 2 -> /dev/pts/17
lrwx------ 1 root root 64 Aug 29 13:16 1 -> /dev/pts/17
lr-x------ 1 root root 64 Aug 29 13:16 0 -> pipe:[3889791]


root@b537ccc2c1ab:/var/log# ls -lrt
-rw-r--r-- 1 root root  11098 Aug 29 13:17 ps.log.20180829131658

root@b537ccc2c1ab:/var/log# ls -lrt /proc/*/fd
/proc/8979/fd:
total 0
l-wx------ 1 root root 64 Aug 29 13:16 3 -> /var/log/ps.log.20180829131658
lrwx------ 1 root root 64 Aug 29 13:16 2 -> /dev/pts/17
lrwx------ 1 root root 64 Aug 29 13:16 1 -> /dev/pts/17
lr-x------ 1 root root 64 Aug 29 13:16 0 -> pipe:[3889791]

如何通过这种日志记录和日志轮换方案实现我的目标?特别是,除了日志轮换之外,我的脚本如何写入较新的日志文件,同时捕获脚本中所有其他命令的错误和输出?

答案1

一旦进程打开文件,您就可以对该文件执行各种操作,例如重命名、截断甚至删除它,但进程仍会打开该文件。一个常见的错误是,当日志文件用尽所有磁盘空间时,人们会删除该文件以试图释放空间。但是,写入日志文件的进程仍将其打开,因此空间不会被释放。只有当进程关闭文件时,分配给该文件的块才会被释放。(这里的解决方法是截断文件,即> logfile。)

在您的情况下,您已重命名该文件,但写入该文件的进程不知道或不关心这一点。

logcheck实用程序有一个copytruncate针对此类情况的选项:它将日志文件复制到轮换名称,然后截断原始日志文件。你可以做同样的事情:

rotateLog() {
    currentsize=$(du -k $LOGFILE | cut -f1)
    if [ $currentsize -ge $MAX_LOGFILE_SIZE ]; then
            ROTATEDLOG=$LOGFILE.$(date +%Y%m%d%H%M%S)
            cp -p $LOGFILE $ROTATEDLOG && true > $LOGFILE
    fi
}

更好的替代方案是修改进程以理解例如关闭并重新打开日志文件的 SIGHUP 信号。请参阅 shelltrap命令来处理此问题。

答案2

基于@wurtel建议的替代解决方案,我让它像这样工作,

#!/bin/bash

LOGFILE="/root/logr/simple.log"

function sighuphandler() {
    exec > >(tee "$LOGFILE") 2>&1
}

trap sighuphandler SIGHUP

LOGPATH="/root/logr"
MAX_LOGFILE_SIZE=5
rotateLog() {
    currentsize=$(du -k $LOGFILE | cut -f1)
    if [ $currentsize -ge $MAX_LOGFILE_SIZE ]; then
            savelog -dn $LOGFILE &>/dev/null
            kill -s SIGHUP $$
    fi
}

sighuphandler
while :; do
    echo "[`date "+%Y-%m-%d %H:%M:%S"`] [INFO] - A computer program can easily produce gibberish - especially if it has been provided with garbage beforehand. This program does something a little different. It takes a block of text as input and works out the proportion of characters within the text according to a chosen order. For example, an order of 2 means the program looks at pairs of letters, an order of 3 means triplets of letters and so on. The software can regurgitate random text that is controlled by the proportion of characters. The results can be quite surprising."

    ls +

    rotateLog

    sleep 5
done

相关内容