当前位置: 首页 > 知识库问答 >
问题:

重定向系统。我们需要一个完整的系统。犯错误

郑正文
2023-03-14

我有一些遗留代码(或者更确切地说,一些我们无法控制但我们必须使用的代码),可以编写大量语句来system.out/err.

与此同时,我们正在使用一个框架,该框架使用一个围绕log4j的自定义日志系统(同样,不幸的是,我们不能控制这个)。

因此,我试图将out and err流重定向到一个将使用日志系统的定制PrintStream。我在读关于系统的书。setLog()系统。setErr()方法,但问题是,我需要编写自己的PrintStream类来封装正在使用的日志系统。那将是一个巨大的头痛。

有没有简单的方法来实现这一点?

共有3个答案

翟俊名
2023-03-14

看看PrintStream提供的构造函数。您可以将日志框架OutputStream直接传递给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被称为:-输出,之后没有调用),因为println将首先将另一个测试字符串传递给写入方法,然后用换行再次调用它。

阎德宇
2023-03-14

您不需要环绕正在使用的自定义日志系统。当应用程序初始化时,只需创建LoggingOutputStream并在系统上设置该流。setOut()和系统。setErr()方法,每个方法的日志记录级别都符合您的要求。从那时起,任何系统。应用程序中遇到的out语句应直接转到日志。

白翰海
2023-03-14

为了补充Rick和Mikhail的解决方案,这是本场景中唯一的选择,我想举一个例子,说明创建自定义输出流如何可能导致不那么容易检测/修复问题。下面是一些代码:

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);
  }
}

这个例子展示了使用自定义输出流的缺陷。为了简单起见,↑()函数使用log4j记录器,但是它可以被任何自定义日志记录工具所取代(比如我的场景中的工具)。主函数创建一个包装自定义输出流的PrintStream,并将输出流设置为指向它。然后它执行一个System.out.println()语句。该语句被重定向到自定义输出流,该语句将重定向到记录器。不幸的是,由于记录器是懒惰初始化的,它将获取控制台输出流的副本(根据定义ConsoleAppender的log4j配置文件)太晚,即,输出流将指向我们刚刚创建的CustomOutputStream,导致重定向循环,从而在运行时产生StackOverflow Error。

现在,有了log4j,这很容易修复:我们只需要在调用System.setOut()之前初始化log4j框架,例如,取消对主函数的第一行的注释。对我来说幸运的是,我必须处理的自定义日志记录工具只是log4j的一个包装,我知道它会在为时已晚之前初始化。然而,对于一个完全自定义的日志记录工具,它在封面下使用System.out/err,除非源代码是可访问的,否则不可能知道是否以及在哪里执行对System.out/err的直接调用,而不是初始化期间对PrintStream引用的调用。对于这种特殊情况,我能想到的唯一工作是检索函数调用堆栈并检测重定向循环,因为↑()函数不应该是递归的。

 类似资料:
  • 在Java /Swing应用程序中,我们将System.out和System.err重定向到一个单独的视图(称为“SystemOutView”),当新的输出出现时,该视图会弹出打开。这对于抛出的异常和都很有效。这个视图可以通过属性文件进行配置(即它是否处于活动状态,以及它是否在输出时弹出打开)。 在这个项目中,我们使用log4j 1.2.15。我发现log4j输出,即使发送到STDOUT,也不会被

  • 本文向大家介绍请不要重复犯我在学习Python和Linux系统上的错误,包括了请不要重复犯我在学习Python和Linux系统上的错误的使用技巧和注意事项,需要的朋友参考一下 本人已经在运维行业工作了将近十年,我最早接触Linux是在大二的样子,那时候只追求易懂,所以就选择了Ubuntu作为学习、使用的对象,它简单、易用、好操作、界面绚丽,对于想接触Linux的新手来说是非常不错的。后来因为个人的

  • 我已经创建了一个摇摆用户界面,并将System.out和System.err重定向到这个代码的文本字段 ConsoleOutputStream通过TearayoutPutStream进行扩展,只要在新线程中不执行任何操作,它就会按预期工作。 然而,我的应用程序执行第三方jar文件,从而创建新线程。当这些线程打印到系统时。它会打印在启动我的应用程序的终端上,而不是打印在我的文本字段中。我已经看过这个

  • Windows 的系统需求 Microsoft Windows XP SP3、Vista、Windows 7、Windows 8、Windows 8.1、Windows 10、Server 2003、 Server 2008、Server 2012 Mac OS X 的系统需求 Mac OS X 10.7 Lion、10.8 Mountain Lion、10.9 Mavericks、10.10 Y

  • Ubuntu Desktop 14.04 OS X Yosemite Windows 8.0, 8.1 and 10 (x86 and x64) 另外 Linux 还需要额外的 GLIBCXX version 3.4.15 or later GLIBC version 2.15 or later

  • Gio.js仅依赖于Three.js。 经过测试,Gio.js在Three.js R90版本下可以很好地运行和使用。 Gio.js可以运行在以下的浏览器环境中: 浏览器 版本 Chrome 64.0.3282.167+ Firefox 58.0.2+ Safari 10.0.1+