过滤 tee 写入文件的输出,但不过滤 tee 写入控制台的输出

Filter output written by tee to file, but not output written by tee to console

我知道有人问过类似的问题,但我一直找不到任何可以解决这种特殊情况的问题。

我有一系列不同的 python 脚本正在使用 logging 库(打印到 stderr)。这些脚本由 bash 脚本运行g 组合在一起,该脚本按顺序调用它们并通过 cron 进行调度。在每个 运行 的末尾,bash 脚本通过电子邮件发送各种退出代码的摘要,并旨在包含各种其他日志消息(即,如果出现 critical 级别的日志记录, 它应该达到我所说的 "cron log").

由于 python 代码本身的日志记录非常冗长,我不想不小心通过电子邮件向自己发送 5-10MB 的日志,我只想将特定的输出写入 cron 日志。我想使用 tee 过滤来自 python 的 stderr 日志记录,并且只将 特定的 消息定向到 cron 日志,但是 所有 条消息仍应发送到控制台。

为了重现性,假设我有以下 bash 脚本:

#!/bin/bash

LOGPFX="BASH"  # if a log msg has this term, put it in CRONLOG
CRONLOG="cronlog.txt"

exec 2> >(grep ${LOGPFX} | tee -a ${CRONLOG})

# run
python test.py

这里是test.py的内容:

import logging

def get_logger():
    logger = logging.getLogger("testlog")
    logger.setLevel(logging.INFO)
    lformat = "%(asctime)s - %(levelname)s - %(module)s - %(funcName)s - %(message)s"

    handler = logging.StreamHandler()
    handler.setLevel(logging.INFO)
    handler.setFormatter(logging.Formatter(lformat))

    logger.addHandler(handler)
    return logger

if __name__ == '__main__':
    logger = get_logger()
    logger.info("Should not appear in console but not cronlog.txt")
    logger.info("[BASH INFO] Should appear in both")

"[BASH INFO] Should appear in both" 消息 确实 成功进入了 cron 日志,但问题是控制台输出中完全省略了第一条日志消息。我如何过滤这些消息,使那些通过 grep 的消息进入控制台和日志,而所有其他消息仅进入控制台?

我知道这是需要更改的行:

exec 2> >(grep ${LOGPFX} | tee -a ${CRONLOG})

cronlog.txt 的内容:

$ cat cronlog.txt 
2017-12-21 08:19:39,267 - INFO - test - <module> - [BASH INFO] Should appear in both

我也测试了这个版本的 bash:

#!/bin/bash

LOGPFX="BASH"
CRONLOG="cronlog.txt"

ftee () {
    echo "I FOUND THIS: "
    echo "" >> ${CRONLOG}
}

exec 2> >(grep ${LOGPFX} | ftee) 2>&1

# run
python test.py

它正确地将所有内容定向到控制台,但没有将正确的消息写入日志("I FOUND THIS: " 行打印一个空字符串)

tee 写入另一个进程替换(在写入之前执行 grep)将达到目的:

# all-caps variable names are used for variables with meaning to the shell
# don't use them for names you assign yourself.
logpfx="BASH"  # if a log msg has this term, put it in CRONLOG
cronlog="cronlog.txt"

exec 3>&2 # backup original stderr on FD 3
exec 2> >(tee -a >(grep "$logpfx" >"$cronlog"))

注意邮寄日志之前,您应该确保关闭文件描述符并让它刷新。因此,在收集完您要记录的内容后发送电子邮件之前,您需要 运行:

exec 2>&3 # restore backup of original FD 3, so tee and grep can exit