Java:同步标准输出和标准错误

我有一个奇怪的问题,如果我能解决它会很好。 出于调试目的(以及其他一些事情),我正在标准输出上编写控制台Java应用程序的日志。 有些东西是标准出来的,有些东西就像错误一样打印在标准错误上。 问题是这两者并不是完全同步的,因此打印线的顺序并不总是正确的。 我想这是因为打印了许多东西,并且发生一个输出的缓冲区已满,所以另一个输出在第一个输出打印之前打印缓冲区。

我想写这个:

syso: aaa syso: bbb syso: ccc syso: ddd syso: eee syserr: --- 

有时打印的是

 aaa bbb ccc --- ddd eee 

有时中间没有新的线,所以它看起来像

 aaa bbb ccc--- ddd eee 

每次我在输出上打印一些东西时,我用相同的输出刷新

 System.out.flush(); 

要么

 System.err.flush(); 

如何解决这个问题呢? 顺便说一下,所有内容都在Eclipse控制台中打印出来。

 public class Util synchronized public static void printToOut(...) out.print(...) synchronized public static void printToErr(...) err.print(...) 

问题是终端仿真器(在您的情况下是Eclipse)负责处理标准输出和应用程序的标准错误。 如果不与终端仿真器通信,您永远不能确定outerr以正确的顺序显示。 因此,我会考虑在err上打印所有内容并将其重定向到文件。 您仍然可以用于干净的用户交互。

然而,对你的问题有一个(非常糟糕但严格的)解决方案:

 System.out.println(...); System.out.flush(); Thread.sleep(100); System.err.println(...); System.err.flush(); Thread.sleep(100); 

您可能需要根据您的配置更改睡眠持续时间!

这是一个长期存在的Eclipse错误: https : //bugs.eclipse.org/bugs/show_bug.cgi?id = 32205 。

我知道这篇文章很古老,但它今天仍然是一个问题,所以这里有另一种解决方案,使用@EserAygün的答案解决问题,但是这种方式不需要你找到并修改你所写的项目中的每个地方System.outSystem.err

创建一个名为EclipseTools的类, EclipseTools包含以下内容(以及所需的包声明和导入):

 public class EclipseTools { private static OutputStream lastStream = null; private static boolean isFixed = false; private static class FixedStream extends OutputStream { private final OutputStream target; public FixedStream(OutputStream originalStream) { target = originalStream; } @Override public void write(int b) throws IOException { if (lastStream!=this) swap(); target.write(b); } @Override public void write(byte[] b) throws IOException { if (lastStream!=this) swap(); target.write(b); } @Override public void write(byte[] b, int off, int len) throws IOException { if (lastStream!=this) swap(); target.write(b, off, len); } private void swap() throws IOException { if (lastStream!=null) { lastStream.flush(); try { Thread.sleep(200); } catch (InterruptedException e) {} } lastStream = this; } @Override public void close() throws IOException { target.close(); } @Override public void flush() throws IOException { target.flush(); } } /** * Inserts a 200ms delay into the System.err or System.out OutputStreams * every time the output switches from one to the other. This prevents * the Eclipse console from showing the output of the two streams out of * order. This function only needs to be called once. */ public static void fixConsole() { if (isFixed) return; isFixed = true; System.setErr(new PrintStream(new FixedStream(System.err))); System.setOut(new PrintStream(new FixedStream(System.out))); } } 

然后,只需在代码的开头调用EclipseTools.fixConsole()一次。 问题解决了。

基本上,这将使用一组自定义流替换两个流System.errSystem.out ,这些流只是将其数据转发到原始流,但是跟踪哪个流写入最后。 如果写入的流发生更改,例如System.err.something(...)后跟System.out.something(...) ,则刷新最后一个流的输出并等待200ms给出Eclipse控制台有时间完成打印。

注意:200ms只是一个粗略的初始值。 如果此代码减少但不能消除问题,请将Thread.sleep的延迟从200增加到更高,直到它工作。 或者,如果此延迟有效但会影响代码的性能(如果经常交替使用流),则可以尝试逐渐减少它,直到开始出现错误为止。

java.lang.System.setErr(java.lang.System.out);

使应用程序使用标准输出作为错误流。

问题在于使用Eclipse控制台。 通常, std out会一次一个地写入字节到控制台,而std err也会写入,但是红色。 但是,该方法不会在返回之前等待所有字节都被写入。 所以,我推荐的是:

 import java.io.OutputStream; import java.io.PrintStream; import java.util.function.IntConsumer; public final class Printer extends PrintStream { public static final Printer out = new Printer( e -> System.out.print((char) e)); public static final Printer err = new Printer( e -> System.err.print((char) e)); private final IntConsumer printer; private static final Object lock = ""; private Printer(IntConsumer printer) { super(new OutputStream() { public void write(int b) { printer.accept(b); } }); this.printer = printer; } public void print(int x) { synchronized (lock) { this.print(Integer.toString(x)); } } public void print(boolean x) { synchronized (lock) { this.print(Boolean.toString(x)); } } public void print(double x) { synchronized (lock) { this.print(Double.toString(x)); } } public void print(float x) { synchronized (lock) { this.print(Float.toString(x)); } } public void print(long x) { synchronized (lock) { this.print(Long.toString(x)); } } public void print(char x) { synchronized (lock) { this.print(Character.toString(x)); } } public void print(char[] x) { synchronized (lock) { StringBuffer str = new StringBuffer(x.length); for (char c : x) { str.append(c); } this.print(str); } } public void print(Object x) { synchronized (lock) { this.print(x.toString()); } } public void print(String x) { synchronized (lock) { x.chars().forEach(printer); } } public void println(int x) { synchronized (lock) { this.print(Integer.toString(x) + "\n"); } } public void println(boolean x) { synchronized (lock) { this.print(Boolean.toString(x) + "\n"); } } public void println(double x) { synchronized (lock) { this.print(Double.toString(x) + "\n"); } } public void println(float x) { synchronized (lock) { this.print(Float.toString(x) + "\n"); } } public void println(long x) { this.print(Long.toString(x) + "\n"); } public void println(char x) { synchronized (lock) { this.print(Character.toString(x) + "\n"); } } public void println(char[] x) { synchronized (lock) { StringBuffer str = new StringBuffer(x.length); for (char c : x) { str.append(c); } this.print(str + "\n"); } } public void println(Object x) { synchronized (lock) { this.print(x.toString() + "\n"); } } public void println(String x) { synchronized (lock) { x.chars().forEach(printer); printer.accept('\n'); } } } 

使用Printer.outPrinter.err而不是System.outSystem.err 。 它仍然有相同的错误,但这效果要好得多。