【问题标题】:How to prepend stdout and stderr output with timestamp when redirecting into log files?重定向到日志文件时,如何在 stdout 和 stderr 输出前加上时间戳?
【发布时间】:2019-07-05 05:12:10
【问题描述】:

在 Linux 中,我在初始化脚本 (SysVInit) 中启动了一个名为 $cmd 的程序。我已经将$cmd 的stdout 和stderr 重定向到两个不同的日志文件中,分别称为$stdout_log$stderr_log。现在我还想在打印到日志文件的每一行前面添加一个时间戳。

我尝试编写一个名为 log_pipe 的函数,如下所示:

log_pipe() {
    while read line; do
        echo [$(date +%Y-%m-%d\ %H:%M:%S)] "$line"
    done
}

然后将我的脚本的输出通过管道传输到这个函数中,然后将它们重定向到日志文件,如下所示:

$cmd | log_pipe >> "$stdout_log" 2>> "$stderr_log" &

我得到的是一个空的 $stdout.log (stdout) 应该没问题,因为 $cmd 通常不会打印任何东西。还有一个 $stderr.log 文件,只有时间戳但没有错误文本。

我的推理错误在哪里?

PS:因为问题存在于初始化脚本中,我只想使用基本的 shell 命令,没有额外的包。

【问题讨论】:

  • 你考虑过'ts'命令吗?坦率地说,不需要 log_pipe() 程序,因为 unix/linux 已经有了 'ts' 命令,它完全可以满足您的要求。执行“$ man ts”,看看是否满足您的要求。例如用法:“$ cmd 2>&1 | ts '[%Y-%m-%d %H:%M:%S %Z]' | tee /your-path/outputs.log > /dev/null 2> &1 &"

标签: shell logging pipe stdout init


【解决方案1】:

在任何 POSIX shell 中,尝试:

{ cmd | log_pipe >>stdout.log; } 2>&1 | log_pipe >>stderr.log

另外,如果您有 GNU awk(有时称为 gawk),那么 log_pipe 可以变得更简单、更快:

log_pipe() {  awk '{print strftime("[%Y-%m-%d %H:%M:%S]"),$0}'; }

示例

作为例子,让我们创建命令cmd

cmd() { echo "This is out"; echo "This is err">&2; }

现在,让我们运行命令并查看输出文件:

$ { cmd | log_pipe >>stdout.log; } 2>&1 | log_pipe >>stderr.log
$ cat stdout.log
[2019-07-04 23:42:20] This is out
$ cat stderr.log
[2019-07-04 23:42:20] This is err

问题

cmd | log_pipe >> "$stdout_log" 2>> "$stderr_log"

以上将标准输出从cmd 重定向到log_pipelog_pipe 的标准输出重定向到$stdout_loglog_pipe 的标准错误重定向到$stderr_log问题是cmd 的标准错误永远不会被重定向。它直接到终端。

例如,考虑这个cmd

cmd() { echo "This is out"; echo "This is err">&2; }

现在,让我们运行命令:

$ cmd | log_pipe >>stdout.log 2>>stderr.log
This is err

我们可以看到This is err 没有发送到文件stderr.log。相反,它出现在终端上。 log_pipe 从未见过它。 stderr.log 仅捕获来自 log_pipe 的错误消息。

【讨论】:

  • 这行得通,但是我的命令必须在后台运行,因为它在初始化脚本中,因此我必须这样做:bash ({ cmd | log_pipe >>stdout.log; } 2>&1 | log_pipe >>stderr.log) & echo $! > "$pid_file" 对吗?但我认为在这种情况下 $pid_file 中的 pid 不是 $cmd 的 pid...
  • 是的,没错。试试({ cmd | log_pipe >>stdout.log; } 2>&1 | log_pipe >>stderr.log) & pgrep cmd > "$pid_file"
【解决方案2】:

在 Bash 中,您还可以使用进程替换重定向到子 shell:

logger.sh

#!/bin/bash
while read -r line; do
   echo "[$(date +%Y-%m-%d\ %H:%M:%S)] $line"
done

重定向

cmd > >(logger.sh > stdout.log) 2> >(logger.sh > stderr.log)

【讨论】:

    【解决方案3】:

    这可行,但我的命令必须在后台运行,因为它在初始化脚本中,因此我必须这样做:

    ({ cmd | log_pipe >>stdout.log; } 2>&1 | log_pipe >>stderr.log) &
    echo $! > "$pid_file"
    

    对吗?

    但我认为在这种情况下 $pid_file 中的 pid 不是 $cmd 的 pid...

    【讨论】:

      猜你喜欢
      • 2020-03-04
      • 1970-01-01
      • 2014-07-22
      • 2019-12-31
      • 2022-01-08
      • 2019-06-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多