使用Bash脚本记录日志

Fer*_*eak 17 bash logging rotation

我有以下问题:

我有一个应用程序,它继续产生输出到stderr和stdout.此应用程序的输出在日志文件中捕获(应用程序重定向为:)&> log.txt.我没有任何选项来为此文件生成正确的日志记录.

现在,我有一个cron作业,它每小时运行一次,除了执行其他操作外,它还尝试将上面的日志文件旋转,通过将其复制到log.txt.1然后创建一个空文件并将其复制到log.txt

看起来像:

cp log.txt log.txt.1
touch /tmp/empty
cp /tmp/empty log.txt
Run Code Online (Sandbox Code Playgroud)

问题是,应用程序仍在写入它,因此我在log.txt.1中得到一些非常奇怪的东西,它从很多垃圾字符开始,实际的日志文件在最后的某个地方.

你有什么想法,如何针对这种特定情况制作正确的日志(我也试过cat log.txt > log.txt.1,不起作用)?使用logrotate这个特定的应用程序不是一个选项,幕后有一个我可能不会改变的整个机制.

谢谢,f.

pep*_*uan 10

好的,这是一个想法,受到http://en.wikibooks.org/wiki/Bourne_Shell_Scripting/Files_and_streams的启发

  1. 制作命名管道:

    mkfifo /dev/mypipe
    
    Run Code Online (Sandbox Code Playgroud)
  2. 将stdout和stderr重定向到命名管道:

    &> /dev/mypipe
    
    Run Code Online (Sandbox Code Playgroud)
  3. 从mypipe读取到一个文件:

    cat < /dev/mypipe > /var/log/log.txt &
    
    Run Code Online (Sandbox Code Playgroud)
  4. 当你需要记录旋转时,杀死猫,旋转日志,然后重新启动猫.

现在,我没有测试过这个.告诉我们它是怎么回事.

注意:您可以为命名管道指定任何名称,例如/ var/tmp/pipe1,/ var/log/pipe,/ tmp/abracadabra等.只需确保日志记录脚本运行之前重新创建管道.


或者,不要使用cat,而是使用简单的脚本文件:

#!/bin/bash

while : ; do
  read line
  printf "%s\n" "$line"
done
Run Code Online (Sandbox Code Playgroud)

此脚本保证每个换行符的输出.(cat可能无法开始输出,直到其缓冲区已满或遇到EOF)


最后 - 和测试 - 尝试

重要说明:请阅读以下@andrew的评论.有几种情况需要注意.

好的!终于可以访问我的Linux机器了.这是如何做:

第1步:制作此刻录机脚本:

#!/bin/bash

LOGFILE="/path/to/log/file"
SEMAPHORE="/path/to/log/file.semaphore"

while : ; do
  read line
  while [[ -f $SEMAPHORE ]]; do
    sleep 1s
  done
  printf "%s\n" "$line" >> $LOGFILE
done
Run Code Online (Sandbox Code Playgroud)

第2步:将录音机投入工作:

  1. 创建命名管道:

    mkfifo $PIPENAME
    
    Run Code Online (Sandbox Code Playgroud)
  2. 将应用程序的STDOUT和STDERR重定向到命名管道:

    ...things... &> $PIPENAME
    
    Run Code Online (Sandbox Code Playgroud)
  3. 启动录像机:

    /path/to/recorder.sh < $PIPENAME &
    
    Run Code Online (Sandbox Code Playgroud)

    您可能希望nohup以上内容使其在注销后继续存在.

  4. 完成!

步骤3:如果需要logrotate,请暂停录像机:

touch /path/to/log/file.semaphore
mv /path/to/log/file /path/to/archive/of/log/file
rm /path/to/log/file.semaphore
Run Code Online (Sandbox Code Playgroud)

我建议将上述步骤放入自己的脚本中.随意将第2行更改为您要使用的任何日志旋转方法.


注意:如果你对C编程很方便,你可能想制作一个简短的C程序来执行它的功能recorder.sh.编译的C程序肯定比nohup-ed分离的bash脚本更轻.


注2: David Newcomb在评论中提供了一个有用的警告:当记录器没有运行时,对管道的写入将阻塞,并可能导致程序无法预测地失败.确保录像机在尽可能短的时间内停机(或旋转).

因此,如果您可以确保旋转发生得非常快,您可以替换sleep(一个只接受整数值的内置命令)/bin/sleep(一个接受浮点值的程序)并将睡眠周期设置为0.5或更短.


iva*_*eev 7

首先,你真的不应该在这里重新发明方轮。您的同事可能反对按每日计划轮换日志,这会自动应用于其中的所有脚本/etc/logrotate.d/- 这可以通过将脚本放置在其他地方来避免。


现在,日志轮转的标准方法(在 参考资料中实现logrotate)也可以由任何其他设施实现。例如,这是一个示例实现bash

MAXLOG=<maximum index of a log copy>
for i in `seq $((MAXLOG-1)) -1 1`; do
    mv "log."{$i,$((i+1))}    #will need to ignore file not found errors here
done 
mv log log.1    # since a file descriptor is linked to an inode rather than path,
                #if you move (or even remove) an open file, the program will continue
                #to write into it as if nothing happened
                #see /sf/ask/365392751/
<make the daemon reopen the log file with the old path>
Run Code Online (Sandbox Code Playgroud)

最后一项是通过发送 SIGHUP 或(不太常见)SIGUSR1 并在守护进程中使用信号处理程序来替换相应的文件描述符或变量来完成的。这样,切换是原子的,因此日志记录可用性不会中断。在 bash 中,这看起来像:

trap { exec &>"$LOGFILE"; } HUP
Run Code Online (Sandbox Code Playgroud)

另一种方法是让写入程序本身在每次写入时跟踪日志大小并进行轮换。这限制了您可以写入的位置以及程序本身支持的循环逻辑的选择。但它的优点是作为一个独立的解决方案,并在每次写入时检查日志大小,而不是按计划检查日志大小。许多语言的标准库都有这样的功能。作为一个嵌入式解决方案,这是在 Apache 中实现的rotatelogs

<your_program> 2>&1 | rotatelogs <opts> <logfile> <rotation_criteria>
Run Code Online (Sandbox Code Playgroud)