ch.qos.logback.core.OutputStreamAppender Maven / Gradle / Ivy
/**
* 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.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 lock = new ReentrantLock(true);
/**
* This is the {@link OutputStream outputStream} where output will be written.
*/
private OutputStream outputStream;
/**
* 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++;
}
// only error free appenders should be activated
if (errors == 0) {
super.start();
}
}
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() {
lock.lock();
try {
closeOutputStream();
super.stop();
} finally {
lock.unlock();
}
}
/**
* Close the underlying {@link OutputStream}.
*/
protected void closeOutputStream() {
if (this.outputStream != null) {
try {
// before closing we have to output out layout'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 encoderInit() {
if (encoder != null && this.outputStream != null) {
try {
encoder.init(outputStream);
} catch (IOException ioe) {
this.started = false;
addStatus(new ErrorStatus(
"Failed to initialize encoder for appender named [" + name + "].",
this, ioe));
}
}
}
void encoderClose() {
if (encoder != null && this.outputStream != null) {
try {
encoder.close();
} 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) {
lock.lock();
try {
// close any previously opened output stream
closeOutputStream();
this.outputStream = outputStream;
if (encoder == null) {
addWarn("Encoder has not been set. Cannot invoke its init method.");
return;
}
encoderInit();
} finally {
lock.unlock();
}
}
protected void writeOut(E event) throws IOException {
this.encoder.doEncode(event);
}
/**
* 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();
}
// the synchronization prevents the OutputStream from being closed while we
// are writing. It also prevents multiple threads from entering the same
// converter. Converters assume that they are in a synchronized block.
lock.lock();
try {
writeOut(event);
} finally {
lock.unlock();
}
} 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;
}
}