我有一个奇怪的问题,如果我能解决它会很好。出于调试目的(以及其他一些事情),我正在标准输出上编写控制台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控制台中。
答案 0 :(得分:9)
问题在于终端仿真器(在您的情况下是Eclipse)负责处理标准输出和应用程序的标准错误。如果不与终端仿真器通信,您永远无法确定out
和err
是否以正确的顺序显示。因此,我会考虑在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.out
或System.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.err
和System.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.