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

org.apache.log4j.WriterAppender Maven / Gradle / Ivy

There is a newer version: 3.0.0-beta2
Show newest version
/*
 * 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 org.apache.log4j.helpers.QuietWriter;
import org.apache.log4j.spi.ErrorHandler;
import org.apache.log4j.spi.LoggingEvent;
import org.apache.logging.log4j.status.StatusLogger;

import java.io.IOException;
import java.io.InterruptedIOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.Writer;


/**
 * WriterAppender appends log events to a {@link Writer} or an
 * {@link OutputStream} depending on the user's choice.
 */
public class WriterAppender extends AppenderSkeleton {
    private static final org.apache.logging.log4j.Logger LOGGER = StatusLogger.getLogger();

    /**
     * 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); } /** * Returns value of the ImmediateFlush option. * @return the value of the immediate flush setting. */ public boolean getImmediateFlush() { return immediateFlush; } /** * 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. * * @param value the value to set the immediate flush setting to. */ public void setImmediateFlush(boolean value) { immediateFlush = value; } /** * Does nothing. */ @Override 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. */ @Override 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. * @return true if appending is allowed, false otherwise. */ protected boolean checkEntryConditions() { if (this.closed) { LOGGER.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 */ @Override public synchronized void close() { if (this.closed) { return; } this.closed = true; writeFooter(); reset(); } /** * Close the underlying {@link 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. LOGGER.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 LOGGER. * @param os The OutputStream. * @return The OutputStreamWriter. */ 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(); } LOGGER.warn("Error initializing output writer."); LOGGER.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. */ @Override public synchronized void setErrorHandler(ErrorHandler eh) { if (eh == null) { LOGGER.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. * @param event The event to log. * * @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. */ @Override 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. * @param event The event to log. * @return true if the writer should be flushed. * * @since 1.2.16 */ protected boolean shouldFlush(final LoggingEvent event) { return immediateFlush; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy