使用 tee 过滤进程输出

💡 原文英文,约700词,阅读约需3分钟。
📝

内容提要

为了同时捕获Java进程的stdout输出和日志文件,可以使用tee命令将输出写入stdout和文件。然而,日志文件可能会无限增长,导致磁盘空间不足。为了解决这个问题,可以使用进程替代来过滤日志文件内容,只将过滤后的输出写入日志文件。这样,完整的stdout输出将被暴露给Kubernetes的日志收集器,只有过滤后的输出会写入日志文件。

🔎

延伸解读

tee 与进程替代的协作机制

文章通过示例展示了 tee 与进程替代的配合:tee 将标准输出同时写入 stdout 和指定文件,而进程替代 >(grep 'OutOfMemoryError' > my-app.log) 则让 tee 写入一个特殊文件,该文件实际由 grep 进程接收。这样,完整输出仍流向 stdout 供 Kubernetes 收集,而日志文件只保留过滤后的内容,实现了输出分流与过滤。

日志无限增长的风险与应对

直接使用 tee 将输出写入日志文件时,文件会无限增长,最终可能耗尽 Pod 的磁盘空间。文章指出,通过进程替代结合 grep 过滤,可以只将关心的行(如 OutOfMemoryError)写入日志文件,从而控制文件大小,同时不影响 stdout 的完整输出。这为资源受限环境提供了一种轻量级解决方案。

进程替代的底层原理

文章通过一个 Java 程序示例解释了进程替代的机制:当使用 <(echo "hello") 时,程序参数被替换为 /dev/fd/11,这是一个指向匿名管道的文件描述符。lsof 输出证实了这一点。这表明进程替代在底层通过管道连接进程与文件描述符,为 stdin/stdout 与文件之间提供了灵活的桥梁。

❓

Q&A

如何同时捕获Java进程的stdout输出和日志文件?

可以使用tee命令,将Java进程的stdout通过管道传递给tee,tee会同时将输出写入stdout和指定的日志文件。例如:java -jar my-app.jar -XX:+ExitOnOutOfMemoryError | tee my-app.log。

使用tee命令记录日志时,日志文件无限增长导致磁盘空间不足怎么办?

可以使用进程替代来过滤日志文件内容,只将过滤后的输出写入日志文件。例如:java -jar my-app.jar -XX:+ExitOnOutOfMemoryError 2>&1 | tee >(grep 'OutOfMemoryError' > my-app.log)。这样完整的stdout输出会暴露给Kubernetes日志收集器,而只有过滤后的输出会写入日志文件。

进程替代在过滤进程输出中起什么作用?

进程替代提供了标准输入和输出流与文件之间的桥梁。>(commands)将文件替换为一个进程,该进程从stdin接收写入的内容;<(commands)将文件替换为一个进程,该进程在stdout上提供内容。在过滤输出时,使用>(grep ...)可以让tee将输出写入grep进程的stdin,从而实现过滤。

如何只将包含特定错误信息的日志写入文件?

可以使用grep命令过滤输出内容。例如:java -jar my-app.jar -XX:+ExitOnOutOfMemoryError 2>&1 | tee >(grep 'OutOfMemoryError' > my-app.log)。这样只有包含'OutOfMemoryError'的行会被写入my-app.log文件。

完整的stdout输出如何被Kubernetes日志收集器获取?

通过tee命令,完整的stdout输出会被写入stdout,从而暴露给Kubernetes的日志收集器。即使使用了进程替代进行过滤,tee仍然会将所有输出写入stdout,因此Kubernetes可以收集到完整的日志。

进程替代在底层是如何实现的?

进程替代通常通过匿名管道实现。例如,当使用<(echo "hello")时,/dev/fd目录下会创建一个文件描述符(如/dev/fd/11),该文件描述符对应一个管道。通过lsof命令可以查看进程打开的文件,确认其类型为PIPE。

🏷️

标签

➡️

继续阅读