当缓冲区填满时或“实时”写入日志文件吗?

当缓冲区填满时或“实时”写入日志文件吗?

我正在运行一个脚本,它通过另一个脚本将其输出管道化;该包装器脚本为日志的每一行添加了一个时间戳。但是,数据是以大约 8000 个字符(大约 180 行)的块写入日志文件的。这导致所有这些行的时间戳完全相同。如果输出信息的底层脚本直接在 shell 中运行,则输出将实时逐行显示。

作为参考,脚本如下所示:

#!/bin/bash
python foo.py | ~/timestamp.sh >> ~/logs/foo.log
exit

时间戳脚本只是在每一行输出中添加一个时间戳(您猜对了)。该脚本包含以下内容:

#!/bin/bash
while read x; do
  echo -n `date +%d/%m/%Y\ %H:%M:%S`;
  echo -n " ";
  echo $x;
done

如果我从等式中删除 timestamp.sh,行为完全相同。我通过运行脚本并使用以下方式监视日志文件来检查这一点tail -f script.log

是否有一个设置可以在将文件输出写入日志文件之前对其进行缓冲?我还有其他方法可以尝试为日志文件的每一行添加时间戳吗?我以前在以前的系统上也曾使用过这种类型的方法,但无论我尝试什么,似乎都无法解决这个问题。

这一切都在 Ubuntu 14.04.4 x64 上

答案1

根本原因是您的进程在这里python使用libc stdout当输出到终端时使用行缓冲但当输出到其他东西(例如这里的管道)时使用块缓冲。

您可以在 Python 代码中通过在每次日志输出后明确刷新缓冲区来解决该问题:

sys.stdout.flush()

或者通过一些技巧来控制缓冲的方式:

stdbuf -oL python foo.py | ~/timestamp.sh >> ~/logs/foo.log

或者

unbuffer python foo.py | ~/timestamp.sh >> ~/logs/foo.log

我建议在代码中明确刷新缓冲区(sys.stdout.flush()),因为您是唯一知道输出应该在哪里刷新的人。行缓冲(stdbuf -oL)的优化程度略低,但考虑到您每行都有时间戳,应该没问题,就性能而言,禁用所有缓冲(unbuffer或 heemayl 的python -u建议)是最糟糕的方法(尽管根据您的 python 代码如何编写其输出,它可能不会被注意到)。

答案2

由于 STDOUT 流默认是块缓冲的(当不进入终端时)python,因此您需要使流无缓冲(或行缓冲)。

python提供了一种使流无缓冲的方法,这是 Pythonic 方法:

python -u foo.py

因此,您的整个命令行变成:

python -u foo.py | ~/timestamp.sh >> ~/logs/foo.log

man python

-u     Force stdin, stdout and stderr to be totally unbuffered

我现在知道了您的脚本,但是在脚本中您可以使用模块刷新某个流sys

sys.stdout.flush()

相关内容