[logback-dev] [JIRA] Created: (LBCORE-143) ConsoleAppender should always write to current System.out / System.err. The underlying outputstream should not be bind statically.

tomliliu (JIRA) noreply-jira at qos.ch
Tue Mar 16 12:18:17 CET 2010


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

                 Key: LBCORE-143
                 URL: http://jira.qos.ch/browse/LBCORE-143
             Project: logback-core
          Issue Type: Bug
          Components: Appender
    Affects Versions: 0.9.18
            Reporter: tomliliu
            Assignee: Logback dev list
         Attachments: ConsoleAppender.java

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&uuml;lc&uuml;
 */

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


}



 

-- 
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: http://jira.qos.ch/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira

        


More information about the logback-dev mailing list