我有一些遗留代码(或者说一些代码,我们无法控制,但我们必须使用)写入大量的报表到System.out /犯错。
与此同时,我们使用的是使用log4j的周围(再次,不幸的是我们不控制这一点)包裹的自定义日志系统的框架。
所以我想重定向并犯错流将使用日志记录系统自定义的PrintStream。 我在读有关System.setLog()
和System.setErr()
方法,但问题是,我需要编写,围绕测井系统包装在用自己的PrintStream类。 这将是一个巨大的头痛。
有没有一种简单的方法来实现这一目标?
只需添加到里克和米哈伊尔的解决方案,这是真正在这种情况下唯一的选择,我想给如何创建一个自定义的OutputStream的例子有可能导致不那么容易检测/修复问题。 下面是一些代码:
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的记录器,但是可以使用任何自定义日志记录功能(如一个在我的情况)来代替。 主函数创建一个封装了CustomOutputStream并设置输出流指向它一个PrintStream。 然后,它执行的System.out.println()语句。 这种说法被重定向到其重定向到一个记录器CustomOutputStream。 不幸的是,由于记录器延迟初始化,将收购控制台输出流的副本(按照其定义是ConsoleAppender log4j的配置文件)太晚了,即,输出流将指向CustomOutputStream我们刚刚创建导致重定向环,从而在运行时的StackOverflowError。
现在,log4j的,这是很容易解决:我们只需要我们称之为System.setOut(),例如前 ,通过取消注释主要功能的第一行初始化log4j的框架。 幸运的是,自定义日志记录工具我要处理的只是周围的log4j的包装,我知道这是为时已晚之前,它会被初始化。 然而,在使用的System.out / ERR的掩护下,一个完全自定义日志记录设施的情况下,除非源代码是可访问的,这是不可能的说,如果哪里到System.out / ERR进行的,而不是调用直接调用在初始化过程中一个PrintStream参考获得的。 唯一的解决办法我能想到的这种特殊情况下将检索函数调用堆栈和检测的重定向循环,因为写()函数不应该是递归的。
你不应该需要环绕您所使用的自定义日志记录系统。 当应用程序初始化,只需要创建一个LoggingOutputStream并设置该流的System.setOut()还是System.setErr()与您为每个渴望的日志记录级别的方法。 从此时开始,在应用中遇到任何的System.out语句应该直接去到日志中。
看看提供的构造函数的PrintStream 。 您可以直接通过日志框架的OutputStream到的PrintStream,然后设置System.out和System.err的使用的PrintStream。
编辑:这是一个简单的测试案例:
public class StreamTest
{
public static class MyOutputStream extends FilterOutputStream
{
public MyOutputStream(OutputStream out)
{
super(out);
}
@Override
public void write(byte[] b, int off, int len) throws IOException
{
byte[] text = "MyOutputStream called: ".getBytes();
super.write(text, 0, text.length);
super.write(b, off, len);
}
}
@Test
public void test()
{
//Any OutputStream-implementation will do for PrintStream, probably
//you'll want to use the one from the logging framework
PrintStream outStream = new PrintStream(new MyOutputStream(System.out), true); //Direct to MyOutputStream, autoflush
System.setOut(outStream);
System.out.println("");
System.out.print("TEST");
System.out.println("Another test");
}
}
输出是:
MyOutputStream called:
MyOutputStream called: TESTMyOutputStream called: Another testMyOutputStream called:
第二行有一个“空”调用(MyOutputStream称为:-output然后就没有后话),因为会的println先通过另一项测试 -string在写方法,然后以换行再次调用它。
只要写你自己的OutputStream,然后将其包装成标准的PrintStream。 OutputStream中只有1个方法必须实现,另一个值得覆盖性能方面的原因。
唯一的技巧是切片字节到单独的日志信息,例如,通过“\ n”的流,但这并不是太难实现了。