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

时间:2011-05-25 08:49:18

标签: java eclipse

我有一个奇怪的问题,如果我能解决它会很好。出于调试目的(以及其他一些事情),我正在标准输出上编写控制台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控制台中。

6 个答案:

答案 0 :(得分:9)

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

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

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

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

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

答案 1 :(得分:2)

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

答案 2 :(得分:2)

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

创建一个名为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控制台有时间完成打印。< / p>

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

答案 3 :(得分:1)

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

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

答案 4 :(得分:0)

public class Util

    synchronized public static void printToOut(...)
        out.print(...)

    synchronized public static void printToErr(...)
        err.print(...)

答案 5 :(得分:0)

The problem lies in the use of the Eclipse Console. Usually, std out will write bytes one at a time to the console, and std err will too, but in red. However, the method does not wait for the bytes to all be written before returning. So, what I recommend is this:

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

Use Printer.out and Printer.err instead of System.out and System.err. It still has the same errors, but this works much better.