| #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; |
| } |
| } |
| } |