#region Apache License // // 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. // #endregion using System; using System.IO; using log4net.Util; using log4net.Layout; using log4net.Core; namespace log4net.Appender { /// /// Sends logging events to a . /// /// /// /// An Appender that writes to a . /// /// /// This appender may be used stand alone if initialized with an appropriate /// writer, however it is typically used as a base class for an appender that /// can open a to write to. /// /// /// Nicko Cadell /// Gert Driesen /// Douglas de la Torre public class TextWriterAppender : AppenderSkeleton { #region Public Instance Constructors /// /// Initializes a new instance of the class. /// /// /// /// Default constructor. /// /// public TextWriterAppender() { } /// /// Initializes a new instance of the class and /// sets the output destination to a new initialized /// with the specified . /// /// The layout to use with this appender. /// The to output to. /// /// /// Obsolete constructor. /// /// [Obsolete("Instead use the default constructor and set the Layout & Writer properties")] public TextWriterAppender(ILayout layout, Stream os) : this(layout, new StreamWriter(os)) { } /// /// Initializes a new instance of the class and sets /// the output destination to the specified . /// /// The layout to use with this appender /// The to output to /// /// The must have been previously opened. /// /// /// /// Obsolete constructor. /// /// [Obsolete("Instead use the default constructor and set the Layout & Writer properties")] public TextWriterAppender(ILayout layout, TextWriter writer) { Layout = layout; Writer = writer; } #endregion #region Public Instance Properties /// /// Gets or set whether the appender will flush at the end /// of each append operation. /// /// /// /// The default behavior is to flush at the end of each /// append operation. /// /// /// If this option is set to false, then the underlying /// stream can defer persisting the logging event 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 /// trade-off 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 bool ImmediateFlush { get { return m_immediateFlush; } set { m_immediateFlush = value; } } /// /// Sets the where the log output will go. /// /// /// /// The specified must be open and writable. /// /// /// The will be closed when the appender /// instance is closed. /// /// /// Note: Logging to an unopened will fail. /// /// virtual public TextWriter Writer { get { return m_qtw; } set { lock(this) { Reset(); if (value != null) { m_qtw = new QuietTextWriter(value, ErrorHandler); WriteHeader(); } } } } #endregion Public Instance Properties #region Override implementation of AppenderSkeleton /// /// This method determines if there is a sense in attempting to append. /// /// /// /// This method checks if an output target has been set and if a /// layout has been set. /// /// /// false if any of the preconditions fail. override protected bool PreAppendCheck() { if (!base.PreAppendCheck()) { return false; } if (m_qtw == null) { // Allow subclass to lazily create the writer PrepareWriter(); if (m_qtw == null) { ErrorHandler.Error("No output stream or file set for the appender named ["+ Name +"]."); return false; } } if (m_qtw.Closed) { ErrorHandler.Error("Output stream for appender named ["+ Name +"] has been closed."); return false; } return true; } /// /// This method is called by the /// method. /// /// The event to log. /// /// /// Writes a log statement to the output stream if the output stream exists /// and is writable. /// /// /// The format of the output will depend on the appender's layout. /// /// override protected void Append(LoggingEvent loggingEvent) { RenderLoggingEvent(m_qtw, loggingEvent); if (m_immediateFlush) { m_qtw.Flush(); } } /// /// This method is called by the /// method. /// /// The array of events to log. /// /// /// This method writes all the bulk logged events to the output writer /// before flushing the stream. /// /// override protected void Append(LoggingEvent[] loggingEvents) { foreach(LoggingEvent loggingEvent in loggingEvents) { RenderLoggingEvent(m_qtw, loggingEvent); } if (m_immediateFlush) { m_qtw.Flush(); } } /// /// Close this appender instance. The underlying stream or writer is also closed. /// /// /// Closed appenders cannot be reused. /// override protected void OnClose() { lock(this) { Reset(); } } /// /// Gets or set the and the underlying /// , if any, for this appender. /// /// /// The for this appender. /// override public IErrorHandler ErrorHandler { get { return base.ErrorHandler; } set { lock(this) { if (value == null) { LogLog.Warn(declaringType, "TextWriterAppender: You have tried to set a null error-handler."); } else { base.ErrorHandler = value; if (m_qtw != null) { m_qtw.ErrorHandler = value; } } } } } /// /// This appender requires a to be set. /// /// true /// /// /// This appender requires a to be set. /// /// override protected bool RequiresLayout { get { return true; } } #endregion Override implementation of AppenderSkeleton #region Protected Instance Methods /// /// Writes the footer and closes the underlying . /// /// /// /// Writes the footer and closes the underlying . /// /// virtual protected void WriteFooterAndCloseWriter() { WriteFooter(); CloseWriter(); } /// /// Closes the underlying . /// /// /// /// Closes the underlying . /// /// virtual protected void CloseWriter() { if (m_qtw != null) { try { m_qtw.Close(); } catch(Exception e) { ErrorHandler.Error("Could not close writer ["+m_qtw+"]", e); // do need to invoke an error handler // at this late stage } } } /// /// Clears internal references to the underlying /// and other variables. /// /// /// /// Subclasses can override this method for an alternate closing behavior. /// /// virtual protected void Reset() { WriteFooterAndCloseWriter(); m_qtw = null; } /// /// Writes a footer as produced by the embedded layout's property. /// /// /// /// Writes a footer as produced by the embedded layout's property. /// /// virtual protected void WriteFooter() { if (Layout != null && m_qtw != null && !m_qtw.Closed) { string f = Layout.Footer; if (f != null) { m_qtw.Write(f); } } } /// /// Writes a header produced by the embedded layout's property. /// /// /// /// Writes a header produced by the embedded layout's property. /// /// virtual protected void WriteHeader() { if (Layout != null && m_qtw != null && !m_qtw.Closed) { string h = Layout.Header; if (h != null) { m_qtw.Write(h); } } } /// /// Called to allow a subclass to lazily initialize the writer /// /// /// /// This method is called when an event is logged and the or /// have not been set. This allows a subclass to /// attempt to initialize the writer multiple times. /// /// virtual protected void PrepareWriter() { } /// /// Gets or sets the where logging events /// will be written to. /// /// /// The where logging events are written. /// /// /// /// This is the where logging events /// will be written to. /// /// protected QuietTextWriter QuietWriter { get { return m_qtw; } set { m_qtw = value; } } #endregion Protected Instance Methods #region Private Instance Fields /// /// This is the where logging events /// will be written to. /// private QuietTextWriter m_qtw; /// /// Immediate flush means that the underlying /// or output stream will be flushed at the end of each append operation. /// /// /// /// Immediate flush is slower but ensures that each append request is /// actually written. If is set to /// false, then there is a good chance that the last few /// logging events are not actually persisted if and when the application /// crashes. /// /// /// The default value is true. /// /// private bool m_immediateFlush = true; #endregion Private Instance Fields #region Private Static Fields /// /// The fully qualified type of the TextWriterAppender class. /// /// /// Used by the internal logger to record the Type of the /// log message. /// private readonly static Type declaringType = typeof(TextWriterAppender); #endregion Private Static Fields /// /// Flushes any buffered log data. /// /// The maximum time to wait for logging events to be flushed. /// True if all logging events were flushed successfully, else false. public override bool Flush(int millisecondsTimeout) { // Nothing to do if ImmediateFlush is true if (m_immediateFlush) return true; // lock(this) will block any Appends while the buffer is flushed. lock (this) { m_qtw.Flush(); } return true; } } }