| #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 System.Collections.Generic; |
| using log4net.Filter; |
| using log4net.Util; |
| using log4net.Layout; |
| using log4net.Core; |
| |
| namespace log4net.Appender; |
| |
| /// <summary> |
| /// Abstract base class implementation of <see cref="IAppender"/>. |
| /// </summary> |
| /// <remarks> |
| /// <para> |
| /// This class provides the code for common functionality, such |
| /// as support for threshold filtering and support for general filters. |
| /// </para> |
| /// <para> |
| /// Appenders can also implement the <see cref="IOptionHandler"/> interface. Therefore |
| /// they would require that the <see cref="IOptionHandler.ActivateOptions()"/> method |
| /// be called after the appenders properties have been configured. |
| /// </para> |
| /// </remarks> |
| /// <author>Nicko Cadell</author> |
| /// <author>Gert Driesen</author> |
| public abstract class AppenderSkeleton : IAppender, IBulkAppender, IOptionHandler, IFlushable |
| { |
| /// <summary> |
| /// Default constructor |
| /// </summary> |
| /// <remarks> |
| /// <para>Empty default constructor</para> |
| /// </remarks> |
| protected AppenderSkeleton() => _errorHandler = new OnlyOnceErrorHandler(GetType().Name); |
| |
| /// <summary> |
| /// Finalizes this appender by calling the implementation's |
| /// <see cref="Close"/> method. |
| /// </summary> |
| /// <remarks> |
| /// <para> |
| /// If this appender has not been closed then the <c>Finalize</c> method |
| /// will call <see cref="Close"/>. |
| /// </para> |
| /// </remarks> |
| ~AppenderSkeleton() |
| { |
| // An appender might be closed then garbage collected. |
| // There is no point in closing twice. |
| if (!_isClosed) |
| { |
| LogLog.Debug(_declaringType, $"Finalizing appender named [{Name}]."); |
| Close(); |
| } |
| } |
| |
| /// <summary> |
| /// Gets or sets the threshold <see cref="Level"/> of this appender. |
| /// Defaults to <see cref="Level.All"/>. |
| /// </summary> |
| /// <value> |
| /// The threshold <see cref="Level"/> of the appender. |
| /// </value> |
| /// <remarks> |
| /// <para> |
| /// All log events with lower level than the threshold level are ignored |
| /// by the appender. |
| /// </para> |
| /// <para> |
| /// In configuration files this option is specified by setting the |
| /// value of the <see cref="Threshold"/> option to a level |
| /// string, such as "DEBUG", "INFO" and so on. |
| /// </para> |
| /// </remarks> |
| public Level Threshold { get; set; } = Level.All; |
| |
| /// <summary> |
| /// Gets or sets the <see cref="IErrorHandler"/> for this appender. |
| /// </summary> |
| /// <value>The <see cref="IErrorHandler"/> of the appender</value> |
| /// <remarks> |
| /// <para> |
| /// The <see cref="AppenderSkeleton"/> provides a default |
| /// implementation for the <see cref="ErrorHandler"/> property. |
| /// </para> |
| /// </remarks> |
| public virtual IErrorHandler ErrorHandler |
| { |
| get => _errorHandler; |
| set |
| { |
| lock (LockObj) |
| { |
| if (value is null) |
| { |
| // We do not throw exception here since the cause is probably a |
| // bad config file. |
| LogLog.Warn(_declaringType, "You have tried to set a null error-handler."); |
| } |
| else |
| { |
| _errorHandler = value; |
| } |
| } |
| } |
| } |
| |
| /// <summary> |
| /// The filter chain. |
| /// </summary> |
| /// <value>The head of the filter chain.</value> |
| /// <remarks> |
| /// <para> |
| /// Returns the head Filter. The Filters are organized in a linked list |
| /// and so all Filters on this Appender are available through the result. |
| /// </para> |
| /// </remarks> |
| public virtual IFilter? FilterHead { get; private set; } |
| |
| /// <summary> |
| /// Gets or sets the <see cref="ILayout"/> for this appender. |
| /// </summary> |
| /// <value>The layout of the appender.</value> |
| /// <remarks> |
| /// <para> |
| /// See <see cref="RequiresLayout"/> for more information. |
| /// </para> |
| /// </remarks> |
| /// <seealso cref="RequiresLayout"/> |
| public virtual ILayout? Layout { get; set; } |
| |
| /// <summary> |
| /// Initialize the appender based on the options set |
| /// </summary> |
| /// <remarks> |
| /// <para> |
| /// This is part of the <see cref="IOptionHandler"/> delayed object |
| /// activation scheme. The <see cref="ActivateOptions"/> method must |
| /// be called on this object after the configuration properties have |
| /// been set. Until <see cref="ActivateOptions"/> is called this |
| /// object is in an undefined state and must not be used. |
| /// </para> |
| /// <para> |
| /// If any of the configuration properties are modified then |
| /// <see cref="ActivateOptions"/> must be called again. |
| /// </para> |
| /// </remarks> |
| public virtual void ActivateOptions() |
| { |
| } |
| |
| /// <summary> |
| /// Gets or sets the name that uniquely identifies this appender. |
| /// </summary> |
| public string Name { get; set; } = string.Empty; |
| |
| /// <summary> |
| /// Closes the appender and releases resources. |
| /// </summary> |
| /// <remarks> |
| /// <para> |
| /// Release any resources allocated within the appender such as file handles, |
| /// network connections, etc. |
| /// </para> |
| /// <para> |
| /// It is a programming error to append to a closed appender. |
| /// </para> |
| /// <para> |
| /// This method cannot be overridden by subclasses. This method |
| /// delegates the closing of the appender to the <see cref="OnClose"/> |
| /// method which must be overridden in the subclass. |
| /// </para> |
| /// </remarks> |
| public void Close() |
| { |
| // This lock prevents the appender being closed while it is still appending |
| lock (LockObj) |
| { |
| if (!_isClosed) |
| { |
| OnClose(); |
| _isClosed = true; |
| } |
| } |
| } |
| |
| /// <summary> |
| /// Performs threshold checks and invokes filters before |
| /// delegating actual logging to the subclasses specific |
| /// <see cref="Append(LoggingEvent)"/> method. |
| /// </summary> |
| /// <param name="loggingEvent">The event to log.</param> |
| /// <remarks> |
| /// <para> |
| /// This method cannot be overridden by derived classes. A |
| /// derived class should override the <see cref="Append(LoggingEvent)"/> method |
| /// which is called by this method. |
| /// </para> |
| /// <para> |
| /// The implementation of this method is as follows: |
| /// </para> |
| /// <para> |
| /// <list type="bullet"> |
| /// <item> |
| /// <description> |
| /// Checks that the severity of the <paramref name="loggingEvent"/> |
| /// is greater than or equal to the <see cref="Threshold"/> of this |
| /// appender.</description> |
| /// </item> |
| /// <item> |
| /// <description> |
| /// Checks that the <see cref="IFilter"/> chain accepts the |
| /// <paramref name="loggingEvent"/>. |
| /// </description> |
| /// </item> |
| /// <item> |
| /// <description> |
| /// Calls <see cref="PreAppendCheck()"/> and checks that |
| /// it returns <c>true</c>.</description> |
| /// </item> |
| /// </list> |
| /// </para> |
| /// <para> |
| /// If all of the above steps succeed then the <paramref name="loggingEvent"/> |
| /// will be passed to the abstract <see cref="Append(LoggingEvent)"/> method. |
| /// </para> |
| /// </remarks> |
| public void DoAppend(LoggingEvent loggingEvent) |
| { |
| // This lock is absolutely critical for correct formatting |
| // of the message in a multi-threaded environment. Without |
| // this, the message may be broken up into elements from |
| // multiple thread contexts (like get the wrong thread ID). |
| |
| lock (LockObj) |
| { |
| if (_isClosed) |
| { |
| ErrorHandler.Error($"Attempted to append to closed appender named [{Name}]."); |
| return; |
| } |
| |
| // prevent re-entry |
| if (_recursiveGuard) |
| { |
| return; |
| } |
| |
| try |
| { |
| _recursiveGuard = true; |
| |
| if (FilterEvent(loggingEvent) && PreAppendCheck()) |
| { |
| Append(loggingEvent); |
| } |
| } |
| catch (Exception ex) when (!ex.IsFatal()) |
| { |
| ErrorHandler.Error("Failed in DoAppend", ex); |
| } |
| finally |
| { |
| _recursiveGuard = false; |
| } |
| } |
| } |
| |
| /// <summary> |
| /// Performs threshold checks and invokes filters before |
| /// delegating actual logging to the subclasses specific |
| /// <see cref="Append(LoggingEvent[])"/> method. |
| /// </summary> |
| /// <param name="loggingEvents">The array of events to log.</param> |
| /// <remarks> |
| /// <para> |
| /// This method cannot be overridden by derived classes. A |
| /// derived class should override the <see cref="Append(LoggingEvent[])"/> method |
| /// which is called by this method. |
| /// </para> |
| /// <para> |
| /// The implementation of this method is as follows: |
| /// </para> |
| /// <para> |
| /// <list type="bullet"> |
| /// <item> |
| /// <description> |
| /// Checks that the severity of the <paramref name="loggingEvents"/> |
| /// is greater than or equal to the <see cref="Threshold"/> of this |
| /// appender.</description> |
| /// </item> |
| /// <item> |
| /// <description> |
| /// Checks that the <see cref="IFilter"/> chain accepts the |
| /// <paramref name="loggingEvents"/>. |
| /// </description> |
| /// </item> |
| /// <item> |
| /// <description> |
| /// Calls <see cref="PreAppendCheck()"/> and checks that |
| /// it returns <c>true</c>.</description> |
| /// </item> |
| /// </list> |
| /// </para> |
| /// <para> |
| /// If all of the above steps succeed then the <paramref name="loggingEvents"/> |
| /// will be passed to the <see cref="Append(LoggingEvent[])"/> method. |
| /// </para> |
| /// </remarks> |
| public void DoAppend(LoggingEvent[] loggingEvents) |
| { |
| loggingEvents.EnsureNotNull(); |
| |
| // This lock is absolutely critical for correct formatting |
| // of the message in a multi-threaded environment. Without |
| // this, the message may be broken up into elements from |
| // multiple thread contexts (like get the wrong thread ID). |
| lock (LockObj) |
| { |
| if (_isClosed) |
| { |
| ErrorHandler.Error("Attempted to append to closed appender named [" + Name + "]."); |
| return; |
| } |
| |
| // prevent re-entry |
| if (_recursiveGuard) |
| { |
| return; |
| } |
| |
| try |
| { |
| _recursiveGuard = true; |
| |
| List<LoggingEvent> filteredEvents = new(loggingEvents.Length); |
| |
| foreach (LoggingEvent loggingEvent in loggingEvents) |
| { |
| if (FilterEvent(loggingEvent)) |
| { |
| filteredEvents.Add(loggingEvent); |
| } |
| } |
| |
| if (filteredEvents.Count > 0 && PreAppendCheck()) |
| { |
| Append(filteredEvents); |
| } |
| } |
| catch (Exception e) when (!e.IsFatal()) |
| { |
| ErrorHandler.Error("Failed in Bulk DoAppend", e); |
| } |
| finally |
| { |
| _recursiveGuard = false; |
| } |
| } |
| } |
| |
| /// <summary> |
| /// Test if the logging event should we output by this appender |
| /// </summary> |
| /// <param name="loggingEvent">the event to test</param> |
| /// <returns><c>true</c> if the event should be output, <c>false</c> if the event should be ignored</returns> |
| /// <remarks> |
| /// <para> |
| /// This method checks the logging event against the threshold level set |
| /// on this appender and also against the filters specified on this |
| /// appender. |
| /// </para> |
| /// <para> |
| /// The implementation of this method is as follows: |
| /// </para> |
| /// <para> |
| /// <list type="bullet"> |
| /// <item> |
| /// <description> |
| /// Checks that the severity of the <paramref name="loggingEvent"/> |
| /// is greater than or equal to the <see cref="Threshold"/> of this |
| /// appender.</description> |
| /// </item> |
| /// <item> |
| /// <description> |
| /// Checks that the <see cref="IFilter"/> chain accepts the |
| /// <paramref name="loggingEvent"/>. |
| /// </description> |
| /// </item> |
| /// </list> |
| /// </para> |
| /// </remarks> |
| protected virtual bool FilterEvent(LoggingEvent loggingEvent) |
| { |
| loggingEvent.EnsureNotNull(); |
| if (!IsAsSevereAsThreshold(loggingEvent.Level)) |
| { |
| return false; |
| } |
| |
| IFilter? f = FilterHead; |
| |
| while (f is not null) |
| { |
| switch (f.Decide(loggingEvent)) |
| { |
| case FilterDecision.Deny: |
| return false; // Return without appending |
| |
| case FilterDecision.Accept: |
| f = null; // Break out of the loop |
| break; |
| |
| case FilterDecision.Neutral: |
| f = f.Next; // Move to next filter |
| break; |
| } |
| } |
| |
| return true; |
| } |
| |
| /// <summary> |
| /// Adds a filter to the end of the filter chain. |
| /// </summary> |
| /// <param name="filter">the filter to add to this appender</param> |
| /// <remarks> |
| /// <para> |
| /// The Filters are organized in a linked list. |
| /// </para> |
| /// <para> |
| /// Setting this property causes the new filter to be pushed onto the |
| /// back of the filter chain. |
| /// </para> |
| /// </remarks> |
| public virtual void AddFilter(IFilter filter) |
| { |
| filter.EnsureNotNull(); |
| if (FilterHead is null) |
| { |
| FilterHead = _tailFilter = filter; |
| } |
| else |
| { |
| _tailFilter!.Next = filter; |
| _tailFilter = filter; |
| } |
| } |
| |
| /// <summary> |
| /// Clears the filter list for this appender. |
| /// </summary> |
| /// <remarks> |
| /// <para> |
| /// Clears the filter list for this appender. |
| /// </para> |
| /// </remarks> |
| public virtual void ClearFilters() => FilterHead = _tailFilter = null; |
| |
| /// <summary> |
| /// Checks if the message level is below this appender's threshold. |
| /// </summary> |
| /// <param name="level"><see cref="Level"/> to test against.</param> |
| /// <returns> |
| /// <c>true</c> if the <paramref name="level"/> meets the <see cref="Threshold"/> |
| /// requirements of this appender. A null level always maps to <c>true</c>, |
| /// the equivalent of <see cref="Level.All"/>. |
| /// </returns> |
| protected virtual bool IsAsSevereAsThreshold(Level? level) => level is null || level >= Threshold; |
| |
| /// <summary> |
| /// Is called when the appender is closed. Derived classes should override |
| /// this method if resources need to be released. |
| /// </summary> |
| /// <remarks> |
| /// <para> |
| /// Releases any resources allocated within the appender such as file handles, |
| /// network connections, etc. |
| /// </para> |
| /// <para> |
| /// It is a programming error to append to a closed appender. |
| /// </para> |
| /// </remarks> |
| protected virtual void OnClose() |
| { |
| // Do nothing by default |
| } |
| |
| /// <summary> |
| /// Subclasses of <see cref="AppenderSkeleton"/> should implement this method |
| /// to perform actual logging. |
| /// </summary> |
| /// <param name="loggingEvent">The event to append.</param> |
| /// <remarks> |
| /// <para> |
| /// A subclass must implement this method to perform |
| /// logging of the <paramref name="loggingEvent"/>. |
| /// </para> |
| /// <para>This method will be called by <see cref="DoAppend(LoggingEvent)"/> |
| /// if all the conditions listed for that method are met. |
| /// </para> |
| /// <para> |
| /// To restrict the logging of events in the appender |
| /// override the <see cref="PreAppendCheck()"/> method. |
| /// </para> |
| /// </remarks> |
| protected abstract void Append(LoggingEvent loggingEvent); |
| |
| /// <summary> |
| /// Append a bulk array of logging events. |
| /// </summary> |
| /// <param name="loggingEvents">the array of logging events</param> |
| /// <remarks> |
| /// <para> |
| /// This base class implementation calls the <see cref="Append(LoggingEvent)"/> |
| /// method for each element in the bulk array. |
| /// </para> |
| /// <para> |
| /// A subclass that can better process a bulk array of events should |
| /// override this method in addition to <see cref="Append(LoggingEvent)"/>. |
| /// </para> |
| /// </remarks> |
| protected virtual void Append(LoggingEvent[] loggingEvents) |
| { |
| foreach (LoggingEvent loggingEvent in loggingEvents.EnsureNotNull()) |
| { |
| Append(loggingEvent); |
| } |
| } |
| |
| /// <summary> |
| /// Appends logging events. |
| /// </summary> |
| /// <param name="loggingEvents">The logging events</param> |
| /// <remarks> |
| /// <para> |
| /// This base class implementation calls the <see cref="Append(LoggingEvent)"/> |
| /// method for each element in the bulk array. |
| /// </para> |
| /// <para> |
| /// A subclass that can better process a bulk array of events should |
| /// override this method in addition to <see cref="Append(LoggingEvent)"/>. |
| /// </para> |
| /// </remarks> |
| protected virtual void Append(IEnumerable<LoggingEvent> loggingEvents) |
| { |
| foreach (LoggingEvent loggingEvent in loggingEvents.EnsureNotNull()) |
| { |
| Append(loggingEvent); |
| } |
| } |
| |
| /// <summary> |
| /// Called before <see cref="Append(LoggingEvent)"/> as a precondition. |
| /// </summary> |
| /// <remarks> |
| /// <para> |
| /// This method is called by <see cref="DoAppend(LoggingEvent)"/> |
| /// before the call to the abstract <see cref="Append(LoggingEvent)"/> method. |
| /// </para> |
| /// <para> |
| /// This method can be overridden in a subclass to extend the checks |
| /// made before the event is passed to the <see cref="Append(LoggingEvent)"/> method. |
| /// </para> |
| /// <para> |
| /// A subclass should ensure that they delegate this call to |
| /// this base class if it is overridden. |
| /// </para> |
| /// </remarks> |
| /// <returns><c>true</c> if the call to <see cref="Append(LoggingEvent)"/> should proceed.</returns> |
| protected virtual bool PreAppendCheck() |
| { |
| if ((Layout is null) && RequiresLayout) |
| { |
| ErrorHandler.Error($"AppenderSkeleton: No layout set for the appender named [{Name}]."); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| /// <summary> |
| /// Renders the <see cref="LoggingEvent"/> to a string. |
| /// </summary> |
| /// <param name="loggingEvent">The event to render.</param> |
| /// <returns>The event rendered as a string.</returns> |
| /// <remarks> |
| /// <para> |
| /// Helper method to render a <see cref="LoggingEvent"/> to |
| /// a string. This appender must have a <see cref="Layout"/> |
| /// set to render the <paramref name="loggingEvent"/> to |
| /// a string. |
| /// </para> |
| /// <para>If there is exception data in the logging event and |
| /// the layout does not process the exception, this method |
| /// will append the exception text to the rendered string. |
| /// </para> |
| /// <para> |
| /// Where possible use the alternative version of this method |
| /// <see cref="RenderLoggingEvent(TextWriter,LoggingEvent)"/>. |
| /// That method streams the rendering onto an existing Writer |
| /// which can give better performance if the caller already has |
| /// a <see cref="TextWriter"/> open and ready for writing. |
| /// </para> |
| /// </remarks> |
| protected string RenderLoggingEvent(LoggingEvent loggingEvent) |
| { |
| lock (LockObj) |
| { |
| // Create the render writer on first use |
| _renderWriter ??= new(System.Globalization.CultureInfo.InvariantCulture); |
| |
| // Reset the writer so we can reuse it |
| _renderWriter.Reset(RenderBufferMaxCapacity, RenderBufferSize); |
| |
| RenderLoggingEvent(_renderWriter, loggingEvent); |
| return _renderWriter.ToString(); |
| } |
| } |
| |
| /// <summary> |
| /// Renders the <see cref="LoggingEvent"/> to a string. |
| /// </summary> |
| /// <param name="loggingEvent">The event to render.</param> |
| /// <param name="writer">The TextWriter to write the formatted event to</param> |
| /// <remarks> |
| /// <para> |
| /// Helper method to render a <see cref="LoggingEvent"/> to |
| /// a string. This appender must have a <see cref="Layout"/> |
| /// set to render the <paramref name="loggingEvent"/> to |
| /// a string. |
| /// </para> |
| /// <para>If there is exception data in the logging event and |
| /// the layout does not process the exception, this method |
| /// will append the exception text to the rendered string. |
| /// </para> |
| /// <para> |
| /// Use this method in preference to <see cref="RenderLoggingEvent(LoggingEvent)"/> |
| /// where possible. If, however, the caller needs to render the event |
| /// to a string then <see cref="RenderLoggingEvent(LoggingEvent)"/> does |
| /// provide an efficient mechanism for doing so. |
| /// </para> |
| /// </remarks> |
| protected void RenderLoggingEvent(TextWriter writer, LoggingEvent loggingEvent) |
| { |
| writer.EnsureNotNull(); |
| if (Layout is null) |
| { |
| throw new InvalidOperationException("A layout must be set"); |
| } |
| |
| if (Layout.IgnoresException) |
| { |
| string? exceptionStr = loggingEvent.EnsureNotNull().GetExceptionString(); |
| if (!string.IsNullOrEmpty(exceptionStr)) |
| { |
| // render the event and the exception |
| Layout.Format(writer, loggingEvent); |
| writer.WriteLine(exceptionStr); |
| } |
| else |
| { |
| // there is no exception to render |
| Layout.Format(writer, loggingEvent); |
| } |
| } |
| else |
| { |
| // The layout will render the exception |
| Layout.Format(writer, loggingEvent); |
| } |
| } |
| |
| /// <summary> |
| /// Tests if this appender requires a <see cref="Layout"/> to be set. |
| /// </summary> |
| /// <remarks> |
| /// <para> |
| /// In the rather exceptional case, where the appender |
| /// implementation admits a layout but can also work without it, |
| /// then the appender should return <c>true</c>. |
| /// </para> |
| /// <para> |
| /// This default implementation always returns <c>false</c>. |
| /// </para> |
| /// </remarks> |
| /// <returns> |
| /// <c>true</c> if the appender requires a layout object, otherwise <c>false</c>. |
| /// </returns> |
| protected virtual bool RequiresLayout => false; |
| |
| /// <summary> |
| /// Flushes any buffered log data. |
| /// </summary> |
| /// <remarks> |
| /// This implementation doesn't flush anything and always returns true |
| /// </remarks> |
| /// <returns><c>True</c> if all logging events were flushed successfully, else <c>false</c>.</returns> |
| public virtual bool Flush(int millisecondsTimeout) => true; |
| |
| /// <summary> |
| /// It is assumed and enforced that errorHandler is never null. |
| /// </summary> |
| /// <remarks> |
| /// <para> |
| /// See <see cref="ErrorHandler"/> for more information. |
| /// </para> |
| /// </remarks> |
| private IErrorHandler _errorHandler; |
| |
| /// <summary> |
| /// The last filter in the filter chain. |
| /// </summary> |
| /// <remarks> |
| /// See <see cref="IFilter"/> for more information. |
| /// </remarks> |
| private IFilter? _tailFilter; |
| |
| /// <summary> |
| /// Flag indicating if this appender is closed. |
| /// </summary> |
| /// <remarks> |
| /// See <see cref="Close"/> for more information. |
| /// </remarks> |
| private bool _isClosed; |
| |
| /// <summary> |
| /// The guard prevents an appender from repeatedly calling its own DoAppend method |
| /// </summary> |
| private bool _recursiveGuard; |
| |
| /// <summary> |
| /// Used for locking actions by this appender. |
| /// </summary> |
| protected object LockObj { get; } = new(); |
| |
| /// <summary> |
| /// StringWriter used to render events |
| /// </summary> |
| private ReusableStringWriter? _renderWriter; |
| |
| /// <summary> |
| /// Initial buffer size |
| /// </summary> |
| private const int RenderBufferSize = 256; |
| |
| /// <summary> |
| /// Maximum buffer size before it is recycled |
| /// </summary> |
| private const int RenderBufferMaxCapacity = 1024; |
| |
| /// <summary> |
| /// The fully qualified type of the AppenderSkeleton class. |
| /// </summary> |
| /// <remarks> |
| /// Used by the internal logger to record the Type of the |
| /// log message. |
| /// </remarks> |
| private static readonly Type _declaringType = typeof(AppenderSkeleton); |
| } |