#region Copyright & License
//
// Copyright 2001-2005 The Apache Software Foundation
//
// Licensed 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 checked 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("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()
{
}
#endregion Protected Instance Methods
///
/// 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; }
}
#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
}
}