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.
/**
* 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 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ü
/**
* 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<E> extends UnsynchronizedAppenderBase<E> {
It is the encoder which is ultimately responsible for writing the event to an OutputStream
. /**
* It is the encoder which is ultimately responsible for writing the event to
* an {@link OutputStream}.
*/
protected Encoder<E> encoder;
All synchronization in this class is done via the lock object.
/**
* All synchronization in this class is done via the lock object.
*/
protected final ReentrantLock lock = new ReentrantLock(false);
This is the outputStream
where output will be written. /**
* 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.
Returns:
/**
* 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.
/**
* 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<E> 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<E> lwe = new LayoutWrappingEncoder<E>();
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.
/**
* Stop this appender instance. The underlying stream or writer is also
* closed.
*
* <p>
* Stopped appenders cannot be reused.
*/
public void stop() {
lock.lock();
try {
closeOutputStream();
super.stop();
} finally {
lock.unlock();
}
}
Close the underlying OutputStream
. /**
* 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 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.
Params: - outputStream –
An already opened OutputStream.
/**
* <p>
* Sets the @link OutputStream} where the log output will go. The specified
* <code>OutputStream</code> must be opened by the user and be writable. The
* <code>OutputStream</code> 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();
}
}
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;
lock.lock();
try {
this.outputStream.write(byteArray);
if (immediateFlush) {
this.outputStream.flush();
}
} finally {
lock.unlock();
}
}
Actual writing occurs here.
Most subclasses of WriterAppender
will need to override this
method.
Since: 0.9.0
/**
* Actual writing occurs here.
* <p>
* Most subclasses of <code>WriterAppender</code> 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();
byte[] byteArray = this.encoder.encode(event);
writeBytes(byteArray);
} 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<E> getEncoder() {
return encoder;
}
public void setEncoder(Encoder<E> encoder) {
this.encoder = encoder;
}
public boolean isImmediateFlush() {
return immediateFlush;
}
public void setImmediateFlush(boolean immediateFlush) {
this.immediateFlush = immediateFlush;
}
}