【问题标题】:Java: How to mirror console output to fileJava:如何将控制台输出镜像到文件
【发布时间】:2012-08-10 20:03:12
【问题描述】:

我需要在 Java 中监视控制台输出,我尝试了几种不同的方式来检索流式传输的输出,但我陷入了一些陷阱(循环相同的输出,只捕获一部分输出,丢失输出) 并决定以不同的方式进行。

我正在为流行游戏 Minecraft 的服务器开发一个 Java 插件,我需要能够监控其他插件的控制台输出。
我认为这样做的一个好方法是将控制台输出重定向到文件,然后设置一个重复的异步任务来检查文件,执行它需要做的任何事情,然后清除文件以获取更多输入。我想我可以通过简单地使用 System.setOut(PrintStream stream); 来做到这一点。以及我可以在 Google 上找到的众多指南之一。

但是有一个问题,这就是我今天在这里提问的原因。我需要控制台输出像平常一样留在控制台上,因此“镜像”。我无法编辑任何插件以在其他地方输出,它需要来自我的 Java 插件可以做的事情,而且仅限于此。当然,也许在对文件的每次预定检查时,我都可以将其全部重新打印回控制台,但这会导致一次打印块,这并不理想。我希望这是可能的。谢谢!

编辑:我认为我没有完全解释清楚。 “插件”的类由我正在开发的程序运行。我无法更改类的运行方式,也无法更改其他插件打印到控制台的方式。 我也不想将控制台输出定向到文件进行日志记录,这个想法只是在解析时将其保存在临时位置。理想情况下,我需要将控制台输出的每一行传递给一个函数 (parseString(String line)),该函数根据行的内容执行操作。另外,我不明白如何正确地逐行读取流内容,所以如果有人对如何阅读有任何想法,请告诉我。 :)

【问题讨论】:

  • java ... foo.jar | tee output.txt
  • 你是怎么运行这个的?在 Linux 平台上,您可以通过控制台将输出通过管道传输到 tee,它可以像控制台一样完成打印到文件的工作。
  • 哦,我的世界模组制作者。我也是其中之一。
  • @Paul:我不能这样做,因为插件实际上是在主程序开始运行后运行的,我也无法编辑它。 Carl:请看我给 Paul 的消息,Wug:Minecraft Bukkit 插件开发,而不是改装 :)

标签: java file console mirror


【解决方案1】:

在更改之前捕获 System.out,并使用 TeeOutputStream:

OutputStream myCaptureStream = ...;
PrintStream original = System.out;
OutptStream outputtee = new TeeOutputStream(originalOut, myCaptureStream);
PrintStream printTee = new PrintStream(outputTee);
System.setOut(printTee);

将输出流 (myCaptureStream) 转换为输入流

BufferedReader 为您提供readLine 方法。

伪代码:

// Feed myCaptureStream to TeeOutputStream
OutputStream myCaptureStream = new PipedOutputStream();

// Prepare to capture data being written to that output stream
InputStream myCaptureAsInputStream = new PipedInputStream(myCaptureStream);
Reader myCaptureReader = new InputStreamReader(myCaptureAsInputStream);
BufferedReader myCaptureBuffered = new BufferedReader(myCaptureReader, 1024);

// This must run on separate reader thread; in spin loop:
myCaptureBuffer.readLine

【讨论】:

  • TeeOutputStream 是一个很好的解决方案,所以 +1,但为这件事拉入所有 apache commons 似乎可能不是最理想的。
  • Apache Commons 既小又优化,也很好地分割成子组件。
  • @Jishaxe 我用谷歌搜索了“java stream line-by-line”并找到了geekality.net/2011/05/23/java-inputstream-line-iterator
  • @Jishaxe 与您的问题相关的更多信息。
【解决方案2】:

您可以将 System.out 和 System.err 更改为对您自己的自定义 PrintStream 的引用。此解决方案是跨平台的,与使用特定于平台的辅助程序可执行文件(例如 tee)不同。

Documentation for System.setOut(PrintStream)

您可以编写自己的 PrintStream 子类,如下所示:

class MirroringPrintStream extends PrintStream
{
    PrintStream first, second;

    public MirroringPrintStream(PrintStream first, PrintStream second)
    {
        // fail now rather than later
        if (first == null || second == null) throw new NullPointerException();

        this.first = first;
        this.second = second;
    }

    @override
    // methods go here (I do believe there are rather a lot of them, but they will all look the same)
}

然后您可以使用System.setOut(new MirroringPrintStream(System.out, myLogStream)),其中myLogStream 是您打开到日志文件的PrintStream。我不确定这将如何处理附加到其他东西会定期截断的文件,您可能想尝试一下。

一切都失败了,去 irc.esper.net 并在#risucraft 中询问它

【讨论】:

  • ...你已经和我打赌了:)。
  • 请注意,这实际上不会编译,您需要做一些工作。首先, PrintStream 没有默认构造函数,所以你必须伪造它。创建一个常规的 PrintStream 并将其构造函数传递给您自己的处理镜像的自定义 OutputStream 可能会更容易,就像使用 OutputStream 一样,您只需要担心我认为的 2 种方法。
  • 永远不要低估 Java 的可扩展性... :)
  • 我还想补充一点,这种方法的一个优点是您可以稍后将文件出站流替换为其他任何内容,例如通过 Internet 传输到您的服务器的流。或者向您发送一封包含摘要的电子邮件......可能性很大。
【解决方案3】:

假设这是对控制台输出的友好接管,为什么不登录到您自己的流 并将其镜像到控制台?

【讨论】:

  • 这是我之前尝试过的,但失败了:) 我不知道如何从流中获取行。如果您可以向我提供示例代码或指向我可能错过的指南的链接,请随时这样做;)
  • 定义您自己的类,该类实现适当的协议,例如“void logMessage(String str)”,并将消息记录到您自己的文件和 System.out。使用它而不是原始流作为控制台活动的目标
猜你喜欢
  • 2010-09-30
  • 1970-01-01
  • 2023-03-27
  • 1970-01-01
  • 1970-01-01
  • 2021-06-05
  • 2020-04-24
  • 1970-01-01
  • 2010-10-12
相关资源
最近更新 更多