簡體   English   中英

如何將 java 日志記錄控制台輸出從 std err 更改為 std out?

[英]How do I change java logging console output from std err to std out?

我正在使用java.util.logging的標准ConsoleHandler並且默認情況下控制台輸出被定向到錯誤流(即System.err )。

如何將控制台輸出更改為輸出流(即System.out )?

我到了

 SimpleFormatter fmt = new SimpleFormatter();
 StreamHandler sh = new StreamHandler(System.out, fmt);
 logger.addHandler(sh);

嗯,我只是被咬了幾次腳,試圖完成這個壯舉。 在谷歌搜索我的方式之前,我設法召喚了以下黑客。 丑陋,但它似乎完成了工作。

public class StdoutConsoleHandler extends ConsoleHandler {
  protected void setOutputStream(OutputStream out) throws SecurityException {
    super.setOutputStream(System.out); // kitten killed here :-(
  }
}

注意:從構造函數調用 setOutputStream() 很誘人,但它確實(正如 Jon Skeet 已經指出的那樣)關閉 System.err。 瘋狂的技能!

我想出了一種方法。 首先刪除默認的控制台處理程序:

setUseParentHandlers(false);

然后子類 ConsoleHandler 並在構造函數中:

setOutputStream(System.out);

Handler consoleHandler = new Handler(){
         @Override
            public void publish(LogRecord record)
            {
                if (getFormatter() == null)
                {
                    setFormatter(new SimpleFormatter());
                }

                try {
                    String message = getFormatter().format(record);
                    if (record.getLevel().intValue() >= Level.WARNING.intValue())
                    {
                        System.err.write(message.getBytes());                       
                    }
                    else
                    {
                        System.out.write(message.getBytes());
                    }
                } catch (Exception exception) {
                    reportError(null, exception, ErrorManager.FORMAT_FAILURE);
                }

            }

            @Override
            public void close() throws SecurityException {}
            @Override
            public void flush(){}
        };

我有一個類似的問題。 我想將 INFO 及以下記錄到System.out ,將 WARNING 及以上記錄到System.err 這是我實施的解決方案:

public class DualConsoleHandler extends StreamHandler {

    private final ConsoleHandler stderrHandler = new ConsoleHandler();

    public DualConsoleHandler() {
        super(System.out, new SimpleFormatter());
    }

    @Override
    public void publish(LogRecord record) {
        if (record.getLevel().intValue() <= Level.INFO.intValue()) {
            super.publish(record);
            super.flush();
        } else {
            stderrHandler.publish(record);
            stderrHandler.flush();
        }
    }
}

當然,例如,您可以通過分解對Level.INFO的硬編碼引用來使其更加靈活。 但這對我來說很有效,可以得到一些基本的雙流日志記錄。 (順便說一句,關於不繼承 ConsoleHandler 以避免關閉System.err的提示非常有用。)

查看ConsoleHandler的文檔和源代碼 - 我相信您可以輕松編寫一個僅使用 System.err 而不是 System.out 的版本。 (說實話,ConsoleHandler 不允許配置它,這是一種恥辱。)

然后,這只是將日志系統配置為以正常方式使用新的 StdoutHandler(或任何您稱之為)的情況。

第 1 步:將父處理程序設置為 false。

log.setUseParentHandlers(false);

第 2 步:添加一個寫入 System.out 的處理程序

log.addHandler(new StreamHandler(System.out, new SimpleFormatter()));

就是這樣..

import java.io.IOException;
import java.util.logging.Logger;
import java.util.logging.SimpleFormatter;
import java.util.logging.StreamHandler;

public class App {

     static final Logger log = Logger.getLogger("com.sample.app.App");

     static void processData() {
          log.info("Started Processing Data");
          log.info("Finished processing data");
     }

     public static void main(String args[]) throws IOException {
          log.setUseParentHandlers(false);

          log.addHandler(new StreamHandler(System.out, new SimpleFormatter()));

          processData();
     }
}

如果仍然有人在那里尋找解決此問題的方法。 這是我最后想到的:我只是繼承了 StreamHandler 並添加了一個額外的參數 MaxLevel,它在 publish() 的開頭被檢查。 如果日志事件的級別大於 MaxLevel,則發布將不再執行。 以下是詳細信息:

MaxlevelStreamHandler.java下面的主類。

package helper;

/**
 * The only difference to the standard StreamHandler is 
 * that a MAXLEVEL can be defined (which then is not published)
 * 
 * @author Kai Goergen
 */

import java.io.PrintStream;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.StreamHandler;

public class MaxlevelStreamHandler extends StreamHandler {

    private Level maxlevel = Level.SEVERE;  // by default, put out everything

    /**
     * The only method we really change to check whether the message
     * is smaller than maxlevel.
     * We also flush here to make sure that the message is shown immediately.
     */
    @Override
    public synchronized void publish(LogRecord record) {
        if (record.getLevel().intValue() > this.maxlevel.intValue()) {
            // do nothing if the level is above maxlevel
        } else {
            // if we arrived here, do what we always do
            super.publish(record);
            super.flush();
        }
    }

    /**
     * getter for maxlevel
     * @return
     */
    public Level getMaxlevel() {
        return maxlevel;
    }

    /**
     * Setter for maxlevel. 
     * If a logging event is larger than this level, it won't be displayed
     * @param maxlevel
     */
    public void setMaxlevel(Level maxlevel) {
        this.maxlevel = maxlevel;
    }

    /** Constructor forwarding */
    public MaxlevelStreamHandler(PrintStream out, Formatter formatter) {
        super(out, formatter);
    }

    /** Constructor forwarding */
    public MaxlevelStreamHandler() {
        super();
    }
}

主類

現在要在 stdout 中顯示一些事件,在 stderr 中顯示一些事件,只需設置兩個 StreamLoggers,一個用於關鍵事件,一個用於所有其他事件,並禁用標准控制台記錄器:

// setup all logs that are smaller than WARNINGS to stdout
MaxlevelStreamHandler outSh = new MaxlevelStreamHandler(System.out, formatter);
outSh.setLevel(Level.ALL);
outSh.setMaxlevel(Level.INFO);
logger.addHandler(outSh);

// setup all warnings to stdout & warnings and higher to stderr
StreamHandler errSh = new StreamHandler(System.err, formatter);
errSh.setLevel(Level.WARNING);
logger.addHandler(errSh);

// remove default console logger
logger.setUseParentHandlers(false);

logger.info("info");
logger.warning("warning");
logger.severe("severe");

希望這有幫助!

更新:我在 super.publish() 之后添加了 super.flush() 以確保立即顯示消息。 之前,我遇到了日志消息總是顯示在最后的問題。 它現在是上面代碼的一部分。

如果您使用 Java 日志記錄,則可以更改默認處理程序:

例如,對於文件:Handler fh = new FileHandler(FILENAME); Logger.getLogger(LOGGER_NAME).addHandler(fh);

如果你想輸出到流,你可以使用StreamHandler,我想你可以用你喜歡的任何輸出流配置它,包括系統流。

ConsoleHandler 將在構建期間抓取System.err的快照。 一種選擇是將全局錯誤流與全局輸出流交換,然后創建 ConsoleHandler。

ConsoleHandler h = null;
final PrintStream err = System.err;
System.setErr(System.out);
try {
    h = new ConsoleHandler(); //Snapshot of System.err
} finally {
    System.setErr(err);
}

這假設代碼有權修改錯誤流,並且沒有其他正在運行的代碼正在訪問錯誤流。 簡而言之,這是一種選擇,但還有更安全的選擇。

當我們創建一個新的 ConsoleHandler 對象時,默認的輸出流是“system.err”。 遺憾的是,Java 沒有為 ConsoleHandler 類提供任何公共方法來設置輸出流。 所以只能在創建對象的時候設置。 由於 ConsoleHandler 類擴展了 StreamHandler,它有一個受保護的方法“setOutputStream”來顯式設置輸出流。 要為 ConsoleHandler 設置輸出流,只需在創建對象的新調用時覆蓋此方法。

ConsoleHandler consoleHandler = new ConsoleHandler (){
            @Override
            protected synchronized void setOutputStream(OutputStream out) throws SecurityException {
                super.setOutputStream(System.out);
            }
        };

如果你設置了 setUseParentHandlers(false); 只有那個班級設置了它。 應用程序中的其他類仍會將其傳遞給 stderr。

只需在構造函數調用 Super(System.out,) 中擴展 StreamHandler &。 這將避免關閉 System.err - 謝謝

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM