blob: 94826073bb73157bc52b5068466725801ad27f89 [file] [log] [blame]
#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
{
/// <summary>
/// Sends logging events to a <see cref="TextWriter"/>.
/// </summary>
/// <remarks>
/// <para>
/// An Appender that writes to a <see cref="TextWriter"/>.
/// </para>
/// <para>
/// 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 <see cref="TextWriter"/> to write to.
/// </para>
/// </remarks>
/// <author>Nicko Cadell</author>
/// <author>Gert Driesen</author>
/// <author>Douglas de la Torre</author>
public class TextWriterAppender : AppenderSkeleton
{
#region Public Instance Constructors
/// <summary>
/// Initializes a new instance of the <see cref="TextWriterAppender" /> class.
/// </summary>
/// <remarks>
/// <para>
/// Default constructor.
/// </para>
/// </remarks>
public TextWriterAppender()
{
}
/// <summary>
/// Initializes a new instance of the <see cref="TextWriterAppender" /> class and
/// sets the output destination to a new <see cref="StreamWriter"/> initialized
/// with the specified <see cref="Stream"/>.
/// </summary>
/// <param name="layout">The layout to use with this appender.</param>
/// <param name="os">The <see cref="Stream"/> to output to.</param>
/// <remarks>
/// <para>
/// Obsolete constructor.
/// </para>
/// </remarks>
[Obsolete("Instead use the default constructor and set the Layout & Writer properties")]
public TextWriterAppender(ILayout layout, Stream os) : this(layout, new StreamWriter(os))
{
}
/// <summary>
/// Initializes a new instance of the <see cref="TextWriterAppender" /> class and sets
/// the output destination to the specified <see cref="StreamWriter" />.
/// </summary>
/// <param name="layout">The layout to use with this appender</param>
/// <param name="writer">The <see cref="TextWriter" /> to output to</param>
/// <remarks>
/// The <see cref="TextWriter" /> must have been previously opened.
/// </remarks>
/// <remarks>
/// <para>
/// Obsolete constructor.
/// </para>
/// </remarks>
[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
/// <summary>
/// Gets or set whether the appender will flush at the end
/// of each append operation.
/// </summary>
/// <value>
/// <para>
/// The default behavior is to flush at the end of each
/// append operation.
/// </para>
/// <para>
/// If this option is set to <c>false</c>, then the underlying
/// stream can defer persisting the logging event to a later
/// time.
/// </para>
/// </value>
/// <remarks>
/// 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.
/// </remarks>
public bool ImmediateFlush
{
get { return m_immediateFlush; }
set { m_immediateFlush = value; }
}
/// <summary>
/// Sets the <see cref="TextWriter"/> where the log output will go.
/// </summary>
/// <remarks>
/// <para>
/// The specified <see cref="TextWriter"/> must be open and writable.
/// </para>
/// <para>
/// The <see cref="TextWriter"/> will be closed when the appender
/// instance is closed.
/// </para>
/// <para>
/// <b>Note:</b> Logging to an unopened <see cref="TextWriter"/> will fail.
/// </para>
/// </remarks>
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
/// <summary>
/// This method determines if there is a sense in attempting to append.
/// </summary>
/// <remarks>
/// <para>
/// This method checks if an output target has been set and if a
/// layout has been set.
/// </para>
/// </remarks>
/// <returns><c>false</c> if any of the preconditions fail.</returns>
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;
}
/// <summary>
/// This method is called by the <see cref="M:AppenderSkeleton.DoAppend(LoggingEvent)"/>
/// method.
/// </summary>
/// <param name="loggingEvent">The event to log.</param>
/// <remarks>
/// <para>
/// Writes a log statement to the output stream if the output stream exists
/// and is writable.
/// </para>
/// <para>
/// The format of the output will depend on the appender's layout.
/// </para>
/// </remarks>
override protected void Append(LoggingEvent loggingEvent)
{
RenderLoggingEvent(m_qtw, loggingEvent);
if (m_immediateFlush)
{
m_qtw.Flush();
}
}
/// <summary>
/// This method is called by the <see cref="M:AppenderSkeleton.DoAppend(LoggingEvent[])"/>
/// method.
/// </summary>
/// <param name="loggingEvents">The array of events to log.</param>
/// <remarks>
/// <para>
/// This method writes all the bulk logged events to the output writer
/// before flushing the stream.
/// </para>
/// </remarks>
override protected void Append(LoggingEvent[] loggingEvents)
{
foreach(LoggingEvent loggingEvent in loggingEvents)
{
RenderLoggingEvent(m_qtw, loggingEvent);
}
if (m_immediateFlush)
{
m_qtw.Flush();
}
}
/// <summary>
/// Close this appender instance. The underlying stream or writer is also closed.
/// </summary>
/// <remarks>
/// Closed appenders cannot be reused.
/// </remarks>
override protected void OnClose()
{
lock(this)
{
Reset();
}
}
/// <summary>
/// Gets or set the <see cref="IErrorHandler"/> and the underlying
/// <see cref="QuietTextWriter"/>, if any, for this appender.
/// </summary>
/// <value>
/// The <see cref="IErrorHandler"/> for this appender.
/// </value>
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;
}
}
}
}
}
/// <summary>
/// This appender requires a <see cref="Layout"/> to be set.
/// </summary>
/// <value><c>true</c></value>
/// <remarks>
/// <para>
/// This appender requires a <see cref="Layout"/> to be set.
/// </para>
/// </remarks>
override protected bool RequiresLayout
{
get { return true; }
}
#endregion Override implementation of AppenderSkeleton
#region Protected Instance Methods
/// <summary>
/// Writes the footer and closes the underlying <see cref="TextWriter"/>.
/// </summary>
/// <remarks>
/// <para>
/// Writes the footer and closes the underlying <see cref="TextWriter"/>.
/// </para>
/// </remarks>
virtual protected void WriteFooterAndCloseWriter()
{
WriteFooter();
CloseWriter();
}
/// <summary>
/// Closes the underlying <see cref="TextWriter"/>.
/// </summary>
/// <remarks>
/// <para>
/// Closes the underlying <see cref="TextWriter"/>.
/// </para>
/// </remarks>
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
}
}
}
/// <summary>
/// Clears internal references to the underlying <see cref="TextWriter" />
/// and other variables.
/// </summary>
/// <remarks>
/// <para>
/// Subclasses can override this method for an alternate closing behavior.
/// </para>
/// </remarks>
virtual protected void Reset()
{
WriteFooterAndCloseWriter();
m_qtw = null;
}
/// <summary>
/// Writes a footer as produced by the embedded layout's <see cref="ILayout.Footer"/> property.
/// </summary>
/// <remarks>
/// <para>
/// Writes a footer as produced by the embedded layout's <see cref="ILayout.Footer"/> property.
/// </para>
/// </remarks>
virtual protected void WriteFooter()
{
if (Layout != null && m_qtw != null && !m_qtw.Closed)
{
string f = Layout.Footer;
if (f != null)
{
m_qtw.Write(f);
}
}
}
/// <summary>
/// Writes a header produced by the embedded layout's <see cref="ILayout.Header"/> property.
/// </summary>
/// <remarks>
/// <para>
/// Writes a header produced by the embedded layout's <see cref="ILayout.Header"/> property.
/// </para>
/// </remarks>
virtual protected void WriteHeader()
{
if (Layout != null && m_qtw != null && !m_qtw.Closed)
{
string h = Layout.Header;
if (h != null)
{
m_qtw.Write(h);
}
}
}
/// <summary>
/// Called to allow a subclass to lazily initialize the writer
/// </summary>
/// <remarks>
/// <para>
/// This method is called when an event is logged and the <see cref="Writer"/> or
/// <see cref="QuietWriter"/> have not been set. This allows a subclass to
/// attempt to initialize the writer multiple times.
/// </para>
/// </remarks>
virtual protected void PrepareWriter()
{
}
/// <summary>
/// Gets or sets the <see cref="log4net.Util.QuietTextWriter"/> where logging events
/// will be written to.
/// </summary>
/// <value>
/// The <see cref="log4net.Util.QuietTextWriter"/> where logging events are written.
/// </value>
/// <remarks>
/// <para>
/// This is the <see cref="log4net.Util.QuietTextWriter"/> where logging events
/// will be written to.
/// </para>
/// </remarks>
protected QuietTextWriter QuietWriter
{
get { return m_qtw; }
set { m_qtw = value; }
}
#endregion Protected Instance Methods
#region Private Instance Fields
/// <summary>
/// This is the <see cref="log4net.Util.QuietTextWriter"/> where logging events
/// will be written to.
/// </summary>
private QuietTextWriter m_qtw;
/// <summary>
/// Immediate flush means that the underlying <see cref="TextWriter" />
/// or output stream will be flushed at the end of each append operation.
/// </summary>
/// <remarks>
/// <para>
/// Immediate flush is slower but ensures that each append request is
/// actually written. If <see cref="ImmediateFlush"/> is set to
/// <c>false</c>, then there is a good chance that the last few
/// logging events are not actually persisted if and when the application
/// crashes.
/// </para>
/// <para>
/// The default value is <c>true</c>.
/// </para>
/// </remarks>
private bool m_immediateFlush = true;
#endregion Private Instance Fields
#region Private Static Fields
/// <summary>
/// The fully qualified type of the TextWriterAppender class.
/// </summary>
/// <remarks>
/// Used by the internal logger to record the Type of the
/// log message.
/// </remarks>
private readonly static Type declaringType = typeof(TextWriterAppender);
#endregion Private Static Fields
/// <summary>
/// Flushes any buffered log data.
/// </summary>
/// <param name="millisecondsTimeout">The maximum time to wait for logging events to be flushed.</param>
/// <returns><c>True</c> if all logging events were flushed successfully, else <c>false</c>.</returns>
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;
}
}
}