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

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

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

【问题讨论】:

    标签: java logging


    【解决方案1】:

    我到了

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

    【讨论】:

    【解决方案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 - 谢谢

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2019-01-08
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多