Uploaded image for project: 'logback'
  1. logback
  2. LOGBACK-136

ConsoleAppender should always write to current System.out / System.err. The underlying outputstream should not be bind statically.

    XMLWordPrintable

Details

    • Icon: Bug Bug
    • Resolution: Fixed
    • Icon: Major Major
    • None
    • None
    • logback-core
    • None

    Description

      Hi logback-dev,

      ConsoleAppender does not work nicely with JUnit.

      Symptom: when running junit tests with logback (default configuration, no logback configuration is provided), only the first test case's output was captured, the subsequent test cases' output were not captured by junit.

      The problem is that console appender binds itself to stdout / stderr statically.

      Illustrate what happens:
      Junit run test 1 -> System.out is set to test1.out -> Logback auto configure -> Console appender is bind to System.out, which is test1.out
      Junit run test 2 -> System.out is set to test2.out -> Console appender is still bind to test1.out -> The output of subsequent test cases are not correctly redirected.

      The ConsoleAppender's behavior is not correct as it should always write to current System.out / System.err.

      I worked out a simple fix and hope it will be useful:

      /**

      • Logback: the generic, reliable, fast and flexible logging framework.
      • Copyright (C) 2000-2009, QOS.ch
      • This library is free software, you can redistribute it and/or modify it under
      • the terms of the GNU Lesser General Public License as published by the Free
      • Software Foundation.
        */
        package ch.qos.logback.core;

      import java.io.IOException;
      import java.io.OutputStream;

      import ch.qos.logback.core.status.Status;
      import ch.qos.logback.core.status.WarnStatus;

      /**

      • ConsoleAppender appends log events to <code>System.out</code> or <code>System.err</code> using a layout specified by
      • the user. The default target is <code>System.out</code>.
      • For more information about this appender, please refer to the online manual at
      • http://logback.qos.ch/manual/appenders.html#ConsoleAppender
      • @author Ceki Gülcü
        */

      public class ConsoleAppender<E> extends WriterAppender<E> {

      public static final String SYSTEM_OUT = "System.out";

      public static final String SYSTEM_ERR = "System.err";

      protected String target = SYSTEM_OUT;

      private interface OutputStreamProvider

      { OutputStream getOutputStream(); }

      private static class SysoutProvider implements OutputStreamProvider {
      @Override
      public OutputStream getOutputStream()

      { return System.out; }

      }

      private static class SyserrProvider implements OutputStreamProvider {
      @Override
      public OutputStream getOutputStream()

      { return System.err; }

      }

      private static class WrapperOutputStream extends OutputStream {

      private final OutputStreamProvider provider;

      WrapperOutputStream(OutputStreamProvider provider)

      { this.provider = provider; }

      @Override
      public void write(int b) throws IOException

      { provider.getOutputStream().write(b); }

      @Override
      public void write(byte[] b, int off, int len) throws IOException { provider.getOutputStream().write(b, off, len); }

      @Override
      public void write(byte[] b) throws IOException { provider.getOutputStream().write(b); }

      @Override
      public void close() throws IOException

      { provider.getOutputStream().close(); }

      @Override
      public void flush() throws IOException

      { provider.getOutputStream().flush(); }

      }
      /**

      • As in most logback components, the default constructor does nothing.
        */
        public ConsoleAppender() {
        }

      /**

      • Sets the value of the <b>Target</b> option. Recognized values are "System.out" and "System.err". Any other value
      • will be ignored.
        */
        public void setTarget(String value) {
        String v = value.trim();

      if (SYSTEM_OUT.equalsIgnoreCase(v))

      { target = SYSTEM_OUT; }

      else if (SYSTEM_ERR.equalsIgnoreCase(v))

      { target = SYSTEM_ERR; }

      else

      { targetWarn(value); }

      }

      /**

      • Returns the current value of the <b>Target</b> property. The default value of the option is "System.out".
      • See also {@link #setTarget}

        .
        */
        public String getTarget()

        { return target; }

      void targetWarn(String val)

      { Status status = new WarnStatus("[" + val + " should be System.out or System.err.", this); status.add(new WarnStatus("Using previously set target, System.out by default.", this)); addStatus(status); }

      public void start() {
      if (target.equals(SYSTEM_OUT))

      { setWriter(createWriter(new WrapperOutputStream(new SysoutProvider()))); }

      else

      { setWriter(createWriter(new WrapperOutputStream(new SyserrProvider()))); }

      super.start();
      }

      /**

      • This method overrides the parent {@link WriterAppender#closeWriter}

        implementation because the console stream is

      • not ours to close.
        */
        protected final void closeWriter() { writeFooter(); }

      }

      Attachments

        Activity

          People

            logback-dev Logback dev list
            tomliliu tomliliu
            Votes:
            1 Vote for this issue
            Watchers:
            3 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: