问题描述
我有一些遗留代码(或者更确切地说是一些代码,我们无法控制,但我们必须使用),它会将大量语句写入system.out / err。
I have some legacy code (or rather some code we don't control but we have to use) that writes a lot of statements to system.out/err.
与此同时,我们正在使用一个框架,该框架使用围绕log4j的自定义日志记录系统(再次,不幸的是我们无法控制它)。
At the same time, we are using a framework that uses a custom logging system wrapped around log4j (again, unfortunately we don't control this).
所以我我试图将out和err流重定向到将使用日志记录系统的自定义PrintStream。我正在阅读 System.setLog()
和 System.setErr()
方法,但问题是我会我需要编写自己的PrintStream类来包装正在使用的日志系统。这将是一个巨大的麻烦。
So I'm trying to redirect the out and err stream to a custom PrintStream that will use the logging system. I was reading about the System.setLog()
and System.setErr()
methods but the problem is that I would need to write my own PrintStream class that wraps around the logging system in use. That would be a huge headache.
有没有一种简单的方法来实现这一目标?
Is there a simple way to achieve this?
推荐答案
只是为了添加Rick和Mikhail的解决方案,这是这个场景中唯一的选择,我想举例说明如何创建自定义OutputStream可能导致不太容易检测/修复问题。这是一些代码:
Just to add to Rick's and Mikhail's solutions, which are really the only option in this scenario, I wanted to give an example of how creating a custom OutputStream can potentially lead to not so easy to detect/fix problems. Here's some code:
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import org.apache.log4j.Logger;
public class RecursiveLogging {
/**
* log4j.properties file:
*
* log4j.rootLogger=DEBUG, A1
* log4j.appender.A1=org.apache.log4j.ConsoleAppender
* log4j.appender.A1.layout=org.apache.log4j.PatternLayout
* log4j.appender.A1.layout.ConversionPattern=%-4r [%t] %-5p %c %x - %m%n
*
*/
public static void main(String[] args) {
// Logger.getLogger(RecursiveLogging.class).info("This initializes log4j!");
System.setOut(new PrintStream(new CustomOutputStream()));
System.out.println("This message causes a stack overflow exception!");
}
}
class CustomOutputStream extends OutputStream {
@Override
public final void write(int b) throws IOException {
// the correct way of doing this would be using a buffer
// to store characters until a newline is encountered,
// this implementation is for illustration only
Logger.getLogger(CustomOutputStream.class).info((char) b);
}
}
此示例显示使用自定义输出流的缺陷。为简单起见,write()函数使用log4j记录器,但这可以替换为任何自定义日志记录工具(例如我的方案中的那个)。 main函数创建一个PrintStream,它包装CustomOutputStream并将输出流设置为指向它。然后它执行System.out.println()语句。此语句将重定向到CustomOutputStream,后者将其重定向到记录器。不幸的是,由于记录器是惰性初始化的,它将获得控制台输出流的副本(根据定义ConsoleAppender的log4j配置文件)太晚,即输出流将指向我们刚刚创建的CustomOutputStream导致重定向循环,因此在运行时得到StackOverflowError。
This example shows the pitfalls of using a custom output stream. For simplicity the write() function uses a log4j logger, but this can be replaced with any custom logging facility (such as the one in my scenario). The main function creates a PrintStream that wraps a CustomOutputStream and set the output stream to point to it. Then it executes a System.out.println() statement. This statement is redirected to the CustomOutputStream which redirects it to a logger. Unfortunately, since the logger is lazy initialized, it will acquire a copy of the console output stream (as per the log4j configuration file which defines a ConsoleAppender) too late, i.e., the output stream will point to the CustomOutputStream we just created causing a redirection loop and thus a StackOverflowError at runtime.
现在,使用log4j这很容易修复:我们只需要在之前初始化log4j框架调用System.setOut(),例如,通过取消注释main函数的第一行。幸运的是,我必须处理的自定义日志记录工具只是log4j的一个包装器,我知道它会在它为时已晚之前进行初始化。但是,对于在封面下使用System.out / err的完全自定义日志记录工具,除非可以访问源代码,否则无法判断是否以及在何处执行对System.out / err的直接调用而不是调用初始化期间获取的PrintStream引用。对于这种特殊情况,我能想到的唯一工作就是检索函数调用堆栈并检测重定向循环,因为write()函数不应该是递归的。
Now, with log4j this is easy to fix: we just need to initialize the log4j framework before we call System.setOut(), e.g., by uncommenting the first line of the main function. Luckily for me, the custom logging facility I have to deal with is just a wrapper around log4j and I know it will get initialized before it's too late. However, in the case of a totally custom logging facility that uses System.out/err under the cover, unless the source code is accessible, it's impossible to tell if and where direct calls to System.out/err are performed instead of calls to a PrintStream reference acquired during initialization. The only work around I can think of for this particular case would be to retrieve the function call stack and detect redirection loops, since the write() functions should not be recursive.
这篇关于重定向System.out和System.err的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!