如何更改来自 stderr 的 java 日志控制台输出以脱颖而出?

Posted

技术标签:

【中文标题】如何更改来自 stderr 的 java 日志控制台输出以脱颖而出?【英文标题】:How do I change java logging console output from std err to std out? 【发布时间】:2008-10-11 15:00:11 【问题描述】:

我使用来自java.util.logging 的标准ConsoleHandler,默认情况下控制台输出被定向到错误流(即System.err)。

如何将控制台输出更改为输出流(即System.out)?

【问题讨论】:

【参考方案1】:

我到了

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

【讨论】:

这不会禁用默认处理程序。只需按照答案***.com/a/2533250/873282【参考方案2】:

嗯,我只是被咬了几次,试图完成这项壮举。在用谷歌搜索我的方式之前,我设法想到了以下黑客攻击。丑陋,但它似乎完成了工作。

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

注意:从构造函数调用 setOutputStream() 很诱人,但它确实(正如 Jon Skeet 已经指出的那样)关闭 System.err。疯狂的技能!

【讨论】:

【参考方案3】:

我想出了一种方法。首先删除默认的控制台处理程序:

setUseParentHandlers(false);

然后继承ConsoleHandler并在构造函数中:

setOutputStream(System.out);

【讨论】:

你最好用自己实现的OutputStream 包裹System.out 以避免在下次调用setOutputStream(...)LogManager.reset() 时关闭它。 另外,请注意不要调用(ConsoleHandler 的)超级构造函数,因为它设置了 System.err,并且在调用 setOutputStream(System.out) 时将关闭它。或者,您可以只继承 StreamHandler 并调用 super( OutputStream, Formatter)【参考方案4】:
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()
        ;

【讨论】:

但是,在现代……有没有办法在不实现自定义处理程序的情况下做到这一点??【参考方案5】:

我遇到了类似的问题。我想将 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 的提示非常有用。)

【讨论】:

完美。不得不说,设计师们并没有完全做到这一点……他们为什么不能预见到这种需求?请注意,它似乎与其他日志框架相同:您的第一个任务?跳过几个难关!【参考方案6】:

第 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();
     

【讨论】:

在应用程序关闭之前我看不到日志。您知道如何实时查看日志吗?【参考方案7】:

查看ConsoleHandler 的文档和源代码 - 我相信您可以轻松编写只使用 System.err 而不是 System.out 的版本。 (说实话,ConsoleHandler 不允许配置它,这很可惜。)

那么这只是将日志系统配置为以正常方式使用新的 StdoutHandler(或您所称的任何名称)的情况。

【讨论】:

我认为ConsoleHandler是默认的,有一个StreamHandler可以打印到任何其他流。 是的,但你想继承 StreamHandler 以避免试图关闭 System.err,我怀疑。【参考方案8】:

如果还有人在寻找解决此问题的方法。 这是我最终想到的:我只是将 StreamHandler 子类化并添加了一个附加参数 MaxLevel,在 publish() 开始时检查。如果 logging event 的 level 大于 MaxLevel,则不会再执行 publish。 以下是详细信息:

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();
    

主类

现在要在标准输出和标准错误中显示一些事件,只需设置两个 StreamLogger,一个用于关键事件,一个用于所有其他事件,并禁用标准控制台记录器:

// 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() 以确保消息立即显示。以前,我遇到过日志消息总是显示在最后的问题。它现在是上面代码的一部分。

【讨论】:

【参考方案9】:

如果您使用 Java 日志记录,您可以更改默认处理程序:

例如,对于文件: 处理程序 fh = new FileHandler(FILENAME); Logger.getLogger(LOGGER_NAME).addHandler(fh);

如果你想输出到一个流,你可以使用 StreamHandler,我想你可以用任何你喜欢的输出流来配置它,包括系统流。

【讨论】:

【参考方案10】:

当我们创建一个新的 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);
            
        ;

【讨论】:

【参考方案11】:

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

这假定代码有权修改错误流并且没有其他正在运行的代码正在访问错误流。简而言之,这是一种选择,但还有更安全的选择。

【讨论】:

【参考方案12】:

如果你设置了 setUseParentHandlers(false);只有那个类有它设置。应用程序中的其他类仍会将其传递给 stderr。

【讨论】:

如果为根日志记录类 (Logger.GLOBAL_LOGGER_NAME) 执行此操作,则所有类的控制台日志记录都将被禁用。【参考方案13】:

只需在构造函数调用 Super(System.out,) 中扩展 StreamHandler &。这将避免关闭 System.err - 谢谢

【讨论】:

以上是关于如何更改来自 stderr 的 java 日志控制台输出以脱颖而出?的主要内容,如果未能解决你的问题,请参考以下文章

Pitest:如何将日志输出重定向到文件?

将带有日期的 stderr 重定向到来自 Cron 的日志文件

将Java 8 GC日志重定向到stderr

如何设置日志记录级别名称?

我应该将消息记录到 stderr 还是 stdout?

使用py4j将Log4j连接到java / python项目中的ipython notebook stderr