org.apache.log4j.WriterAppender Maven / Gradle / Ivy
Show all versions of reload4j Show documentation
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.log4j;
import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;
import org.apache.log4j.helpers.LogLog;
import org.apache.log4j.helpers.QuietWriter;
import org.apache.log4j.spi.ErrorHandler;
import org.apache.log4j.spi.LoggingEvent;
// Contibutors: Jens Uwe Pipka
// Ben Sandee
/**
* WriterAppender appends log events to a {@link java.io.Writer} or an
* {@link java.io.OutputStream} depending on the user's choice.
*
* @author Ceki Gülcü
* @since 1.1
*/
public class WriterAppender extends AppenderSkeleton {
/**
* Immediate flush means that the underlying writer or output stream will be
* flushed at the end of each append operation unless shouldFlush() is
* overridden. Immediate flush is slower but ensures that each append request is
* actually written. If immediateFlush
is set to
* false
, then there is a good chance that the last few logs events
* are not actually written to persistent media if and when the application
* crashes.
*
*
* The immediateFlush
variable is set to true
by
* default.
*
*/
protected boolean immediateFlush = true;
/**
* The encoding to use when writing.
*
* The encoding
variable is set to null by default
* which results in the utilization of the system's default encoding.
*/
protected String encoding;
/**
* This is the {@link QuietWriter quietWriter} where we will write to.
*/
protected QuietWriter qw;
/**
* This default constructor does nothing.
*/
public WriterAppender() {
}
/**
* Instantiate a WriterAppender and set the output destination to a new
* {@link OutputStreamWriter} initialized with os
as its
* {@link OutputStream}.
*/
public WriterAppender(Layout layout, OutputStream os) {
this(layout, new OutputStreamWriter(os));
}
/**
* Instantiate a WriterAppender and set the output destination to
* writer
.
*
*
* The writer
must have been previously opened by the user.
*/
public WriterAppender(Layout layout, Writer writer) {
this.layout = layout;
this.setWriter(writer);
}
/**
* If the ImmediateFlush option is set to true
, the appender
* will flush at the end of each write. This is the default behavior. If the
* option is set to false
, then the underlying stream can defer
* writing to physical medium to a later time.
*
*
* Avoiding the flush operation at the end of each append results in a
* performance gain of 10 to 20 percent. However, there is safety tradeoff
* involved in skipping flushing. Indeed, when flushing is skipped, then it is
* likely that the last few log events will not be recorded on disk when the
* application exits. This is a high price to pay even for a 20% performance
* gain.
*/
public void setImmediateFlush(boolean value) {
immediateFlush = value;
}
/**
* Returns value of the ImmediateFlush option.
*/
public boolean getImmediateFlush() {
return immediateFlush;
}
/**
* Does nothing.
*/
public void activateOptions() {
}
/**
* This method is called by the {@link AppenderSkeleton#doAppend} method.
*
*
* If the output stream exists and is writable then write a log statement to the
* output stream. Otherwise, write a single warning message to
* System.err
.
*
*
* The format of the output will depend on this appender's layout.
*
*/
public void append(LoggingEvent event) {
// Reminder: the nesting of calls is:
//
// doAppend()
// - check threshold
// - filter
// - append();
// - checkEntryConditions();
// - subAppend();
if (!checkEntryConditions()) {
return;
}
subAppend(event);
}
/**
* This method determines if there is a sense in attempting to append.
*
*
* It checks whether there is a set output target and also if there is a set
* layout. If these checks fail, then the boolean value false
is
* returned.
*/
protected boolean checkEntryConditions() {
if (this.closed) {
LogLog.warn("Not allowed to write to a closed appender.");
return false;
}
if (this.qw == null) {
errorHandler.error("No output stream or file set for the appender named [" + name + "].");
return false;
}
if (this.layout == null) {
errorHandler.error("No layout set for the appender named [" + name + "].");
return false;
}
return true;
}
/**
* Close this appender instance. The underlying stream or writer is also closed.
*
*
* Closed appenders cannot be reused.
*
* @see #setWriter
* @since 0.8.4
*/
public synchronized void close() {
if (this.closed)
return;
this.closed = true;
writeFooter();
reset();
}
/**
* Close the underlying {@link java.io.Writer}.
*/
protected void closeWriter() {
if (qw != null) {
try {
qw.close();
} catch (IOException e) {
if (e instanceof InterruptedIOException) {
Thread.currentThread().interrupt();
}
// There is do need to invoke an error handler at this late
// stage.
LogLog.error("Could not close " + qw, e);
}
}
}
/**
* Returns an OutputStreamWriter when passed an OutputStream. The encoding used
* will depend on the value of the encoding
property. If the
* encoding value is specified incorrectly the writer will be opened using the
* default system encoding (an error message will be printed to the loglog.
*/
protected OutputStreamWriter createWriter(OutputStream os) {
OutputStreamWriter retval = null;
String enc = getEncoding();
if (enc != null) {
try {
retval = new OutputStreamWriter(os, enc);
} catch (IOException e) {
if (e instanceof InterruptedIOException) {
Thread.currentThread().interrupt();
}
LogLog.warn("Error initializing output writer.");
LogLog.warn("Unsupported encoding?");
}
}
if (retval == null) {
retval = new OutputStreamWriter(os);
}
return retval;
}
public String getEncoding() {
return encoding;
}
public void setEncoding(String value) {
encoding = value;
}
/**
* Set the {@link ErrorHandler} for this WriterAppender and also the underlying
* {@link QuietWriter} if any.
*/
public synchronized void setErrorHandler(ErrorHandler eh) {
if (eh == null) {
LogLog.warn("You have tried to set a null error-handler.");
} else {
this.errorHandler = eh;
if (this.qw != null) {
this.qw.setErrorHandler(eh);
}
}
}
/**
*
* Sets the Writer where the log output will go. The specified Writer must be
* opened by the user and be writable.
*
*
* The java.io.Writer
will be closed when the appender instance is
* closed.
*
*
*
* WARNING: Logging to an unopened Writer will fail.
*
*
* @param writer An already opened Writer.
*/
public synchronized void setWriter(Writer writer) {
reset();
this.qw = new QuietWriter(writer, errorHandler);
// this.tp = new TracerPrintWriter(qw);
writeHeader();
}
/**
* Actual writing occurs here.
*
*
* Most subclasses of WriterAppender
will need to override this
* method.
*
* @since 0.9.0
*/
protected void subAppend(LoggingEvent event) {
this.qw.write(this.layout.format(event));
if (layout.ignoresThrowable()) {
String[] s = event.getThrowableStrRep();
if (s != null) {
int len = s.length;
for (int i = 0; i < len; i++) {
this.qw.write(s[i]);
this.qw.write(Layout.LINE_SEP);
}
}
}
if (shouldFlush(event)) {
this.qw.flush();
}
}
/**
* The WriterAppender requires a layout. Hence, this method returns
* true
.
*/
public boolean requiresLayout() {
return true;
}
/**
* Clear internal references to the writer and other variables.
*
* Subclasses can override this method for an alternate closing behavior.
*/
protected void reset() {
closeWriter();
this.qw = null;
// this.tp = null;
}
/**
* Write a footer as produced by the embedded layout's {@link Layout#getFooter}
* method.
*/
protected void writeFooter() {
if (layout != null) {
String f = layout.getFooter();
if (f != null && this.qw != null) {
this.qw.write(f);
this.qw.flush();
}
}
}
/**
* Write a header as produced by the embedded layout's {@link Layout#getHeader}
* method.
*/
protected void writeHeader() {
if (layout != null) {
String h = layout.getHeader();
if (h != null && this.qw != null)
this.qw.write(h);
}
}
/**
* Determines whether the writer should be flushed after this event is written.
*
* @since 1.2.16
*/
protected boolean shouldFlush(final LoggingEvent event) {
return immediateFlush;
}
}