2011-05-25 137 views
9

我有一個奇怪的問題,這將是很好,如果我能解決它。爲了調試目的(以及其他一些事情),我正在編寫標準輸出上的控制檯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控制檯。

+1

你有沒有考慮過使用像Log4J這樣的專用日誌框架呢? – 2011-05-25 08:57:20

+1

@Péter當然,這將是一種選擇,但我寧願如果有一個簡單的解決方案來解決這個問題(以備將來參考,如果沒有其他)。 – Ivan 2011-05-25 08:59:39

回答

0
public class Util 

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

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

這不起作用。 (至少在Ubuntu的12.04與Java 1.6) – Ankit 2013-02-28 23:22:23

+3

這是可能的;即使我們在JVM中同步這兩個流,它們仍然可以在終端設備上異步出現。 – irreputable 2013-03-01 00:44:46

1

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

使得應用程序使用標準的輸出作爲誤差流。

+2

這種方式我不能在標準輸出和標準錯誤之間作出區別(它們都是以黑色打印,而不是像往常一樣以黑紅色打印)。 – Ivan 2011-05-25 09:06:56

8

問題是終端模擬器(在你的情況下,Eclipse)有責任處理你的應用程序的標準輸出和標準錯誤。如果沒有與終端仿真器進行通信,您將無法確定outerr以正確的順序顯示。因此,我會考慮在err上打印所有內容並將其重定向到一個文件。您仍然可以使用out進行乾淨的用戶交互。

然而,有一個(非常糟糕,但嚴格)解決問題的方法:

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

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

您可能必須根據您的配置來改變睡眠時間!

+0

爲什麼這個解決方案非常糟糕? – 2016-12-21 20:48:15

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控制檯有時間完成打印。

注意:200ms只是一個粗略的初始值。如果此代碼減少但不能解決問題,請將Thread.sleep中的延遲從200增加到某個更高的值,直到其工作。或者,如果這種延遲工作,但會影響代碼的性能(如果您經常交換流),您可以逐漸減少它,直到您開始出現錯誤。

0

問題在於使用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。它仍然有相同的錯誤,但是這種方法效果更好。

相關問題