All Downloads are FREE. Search and download functionalities are using the official Maven repository.

ch.qos.logback.core.OutputStreamAppender Maven / Gradle / Ivy

There is a newer version: 1.5.12
Show newest version
/**
 * Logback: the reliable, generic, fast and flexible logging framework.
 * Copyright (C) 1999-2015, QOS.ch. All rights reserved.
 *
 * This program and the accompanying materials are dual-licensed under
 * either the terms of the Eclipse Public License v1.0 as published by
 * the Eclipse Foundation
 *
 *   or (per the licensee's choosing)
 *
 * under the terms of the GNU Lesser General Public License version 2.1
 * as published by the Free Software Foundation.
 */
package ch.qos.logback.core;

import static ch.qos.logback.core.CoreConstants.CODES_URL;

import java.io.IOException;
import java.io.OutputStream;
import java.util.concurrent.locks.ReentrantLock;

import ch.qos.logback.core.encoder.Encoder;
import ch.qos.logback.core.encoder.LayoutWrappingEncoder;
import ch.qos.logback.core.rolling.LengthCounter;
import ch.qos.logback.core.spi.DeferredProcessingAware;
import ch.qos.logback.core.status.ErrorStatus;

/**
 * OutputStreamAppender appends events to a {@link OutputStream}. This class
 * provides basic services that other appenders build upon.
 * 
 * For more information about this appender, please refer to the online manual
 * at http://logback.qos.ch/manual/appenders.html#OutputStreamAppender
 * 
 * @author Ceki Gülcü
 */
public class OutputStreamAppender extends UnsynchronizedAppenderBase {

    /**
     * It is the encoder which is ultimately responsible for writing the event to an
     * {@link OutputStream}.
     */
    protected Encoder encoder;

    /**
     * All synchronization in this class is done via the lock object.
     */
    protected final ReentrantLock streamWriteLock = new ReentrantLock(false);

    /**
     * This is the {@link OutputStream outputStream} where output will be written.
     */
    private OutputStream outputStream;

    boolean immediateFlush = true;

    /**
     * The underlying output stream used by this appender.
     * 
     * @return
     */
    public OutputStream getOutputStream() {
        return outputStream;
    }

    /**
     * Checks that requires parameters are set and if everything is in order,
     * activates this appender.
     */
    public void start() {
        int errors = 0;
        if (this.encoder == null) {
            addStatus(new ErrorStatus("No encoder set for the appender named \"" + name + "\".", this));
            errors++;
        }

        if (this.outputStream == null) {
            addStatus(new ErrorStatus("No output stream set for the appender named \"" + name + "\".", this));
            errors++;
        }

        if (encoder == null) {
            addWarn("Encoder has not been set. Cannot invoke its init method.");
            errors++;
        }


        // only error free appenders should be activated
        if (errors == 0) {
            super.start();
            encoderInit();
        }
    }

    public void setLayout(Layout layout) {
        addWarn("This appender no longer admits a layout as a sub-component, set an encoder instead.");
        addWarn("To ensure compatibility, wrapping your layout in LayoutWrappingEncoder.");
        addWarn("See also " + CODES_URL + "#layoutInsteadOfEncoder for details");
        LayoutWrappingEncoder lwe = new LayoutWrappingEncoder();
        lwe.setLayout(layout);
        lwe.setContext(context);
        this.encoder = lwe;
    }

    @Override
    protected void append(E eventObject) {
        if (!isStarted()) {
            return;
        }

        subAppend(eventObject);
    }

    /**
     * Stop this appender instance. The underlying stream or writer is also closed.
     * 
     * 

* Stopped appenders cannot be reused. */ public void stop() { if(!isStarted()) return; streamWriteLock.lock(); try { closeOutputStream(); super.stop(); } finally { streamWriteLock.unlock(); } } /** * Close the underlying {@link OutputStream}. */ protected void closeOutputStream() { if (this.outputStream != null) { try { // before closing we have to output out encooder's footer encoderClose(); this.outputStream.close(); this.outputStream = null; } catch (IOException e) { addStatus(new ErrorStatus("Could not close output stream for OutputStreamAppender.", this, e)); } } } void encoderClose() { if (encoder != null && this.outputStream != null) { try { byte[] footer = encoder.footerBytes(); writeBytes(footer); } catch (IOException ioe) { this.started = false; addStatus(new ErrorStatus("Failed to write footer for appender named [" + name + "].", this, ioe)); } } } /** *

* Sets the @link OutputStream} where the log output will go. The specified * OutputStream must be opened by the user and be writable. The * OutputStream will be closed when the appender instance is * closed. * * @param outputStream An already opened OutputStream. */ public void setOutputStream(OutputStream outputStream) { streamWriteLock.lock(); try { // close any previously opened output stream closeOutputStream(); this.outputStream = outputStream; // the first call to setOutputStream() is made on a non started appender // However, in subsequent calls to setOutputStream() the appender will be in // started state. In subsequent calls, in particular when opening a file after rollover, // we have to output the header hence the call to encoderInit(). if(isStarted()) { encoderInit(); } } finally { streamWriteLock.unlock(); } } void encoderInit() { if (encoder != null && this.outputStream != null) { try { byte[] header = encoder.headerBytes(); writeBytes(header); } catch (IOException ioe) { this.started = false; addStatus( new ErrorStatus("Failed to initialize encoder for appender named [" + name + "].", this, ioe)); } } } protected void writeOut(E event) throws IOException { byte[] byteArray = this.encoder.encode(event); writeBytes(byteArray); } private void writeBytes(byte[] byteArray) throws IOException { if (byteArray == null || byteArray.length == 0) return; streamWriteLock.lock(); try { // guard against appender having been stop() in parallel // note that the encoding step is performed outside the protection of the streamWriteLock if(isStarted()) { writeByteArrayToOutputStreamWithPossibleFlush(byteArray); updateByteCount(byteArray); } } finally { streamWriteLock.unlock(); } } protected void updateByteCount(byte[] byteArray) { } /** * A simple method to write to an outputStream and flush the stream if immediateFlush is set to true. * * @since 1.3.9/1.4.9 */ protected final void writeByteArrayToOutputStreamWithPossibleFlush(byte[] byteArray) throws IOException { this.outputStream.write(byteArray); if (immediateFlush) { this.outputStream.flush(); } } /** * Actual writing occurs here. *

* Most subclasses of WriterAppender will need to override this * method. * * @since 0.9.0 */ protected void subAppend(E event) { if (!isStarted()) { return; } try { // this step avoids LBCLASSIC-139 if (event instanceof DeferredProcessingAware) { ((DeferredProcessingAware) event).prepareForDeferredProcessing(); } writeOut(event); } catch (IOException ioe) { // as soon as an exception occurs, move to non-started state // and add a single ErrorStatus to the SM. this.started = false; addStatus(new ErrorStatus("IO failure in appender", this, ioe)); } } public Encoder getEncoder() { return encoder; } public void setEncoder(Encoder encoder) { this.encoder = encoder; } public boolean isImmediateFlush() { return immediateFlush; } public void setImmediateFlush(boolean immediateFlush) { this.immediateFlush = immediateFlush; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy