[ 
https://issues.apache.org/jira/browse/LOG4NET-407?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=13830024#comment-13830024
 ] 

Michael Goldfinger commented on LOG4NET-407:
--------------------------------------------

This is my implementation
{code:java}
namespace Goldfinger.Appender
{
        using System;
        using System.Threading.Tasks;

        using log4net.Appender;
        using log4net.Core;
        using log4net.Util;

        /// <summary>
        ///     Appender that forwards LoggingEvents asynchronously
        /// </summary>
        /// <remarks>
        ///     This appender forwards LoggingEvents to a list of attached 
appenders.
        ///     The events are forwarded asynchronously using the PTL.
        ///     This allows the calling thread to be released quickly. This
        ///     implementation does guarantee the ordering of events deliverted 
to
        ///     the attached appenders, however it does not guarantee that all 
events
        ///     got logged if the application exits and there are events in the 
queue.
        ///     To force all events to be logged the last command of an 
application
        ///     before exit should be an LogManager.Shutdown() call.
        /// </remarks>
        public sealed class AsyncAppender : IBulkAppender, IOptionHandler, 
IAppenderAttachable
        {
                #region Static Fields

                /// <summary>
                /// The declaring type.
                /// </summary>
                private static readonly Type DeclaringType = 
typeof(AsyncAppender);

                #endregion

                #region Fields

                private AppenderAttachedImpl _appenderAttachedImpl;

                private Task _asyncLogTask;

                private bool _closed;

                private IErrorHandler _errorHandler;

                private FixFlags _fixFlags = FixFlags.All;

                private string _name;

                private bool _recursiveGuard;

                #endregion

                #region Constructors and Destructors

                /// <summary>
                ///     Initializes a new instance of the <see 
cref="AsyncAppender"/> class.
                /// </summary>
                public AsyncAppender()
                {
                        _errorHandler = new 
OnlyOnceErrorHandler(GetType().Name);

                        // Initialise the task for the async operation.
                        // There is no need to do anything now, just make sure 
the Task got scheduled.
                        _asyncLogTask = new Task(() => { });
                        _asyncLogTask.Start();
                }

                /// <summary>
                /// Finalizes an instance of the <see cref="AsyncAppender"/> 
class. 
                /// </summary>
                ~AsyncAppender()
                {
                        // An appender might be closed then garbage collected. 
                        // There is no point in closing twice.
                        if (!_closed)
                        {
                                LogLog.Debug(DeclaringType, "Finalizing 
appender named [" + _name + "].");
                                Close();
                        }
                }

                #endregion

                #region Public Properties

                /// <summary>
                ///     Gets the known appenders.
                /// </summary>
                public AppenderCollection Appenders
                {
                        get
                        {
                                lock (this)
                                {
                                        return _appenderAttachedImpl == null ? 
AppenderCollection.EmptyCollection : _appenderAttachedImpl.Appenders;
                                }
                        }
                }

                /// <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 IErrorHandler ErrorHandler
                {
                        get
                        {
                                return _errorHandler;
                        }

                        set
                        {
                                lock (this)
                                {
                                        if (value == 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>
                ///     Gets or sets the fix.
                /// </summary>
                public FixFlags Fix
                {
                        get
                        {
                                return _fixFlags;
                        }

                        set
                        {
                                _fixFlags = value;
                        }
                }

                /// <summary>
                ///     Gets or sets the name.
                /// </summary>
                public string Name
                {
                        get
                        {
                                return _name;
                        }

                        set
                        {
                                _name = value;
                        }
                }

                #endregion

                #region Public Methods and Operators

                /// <summary>
                ///     The activate options.
                /// </summary>
                public void ActivateOptions()
                {
                }

                /// <summary>
                /// Add a new appender.
                /// </summary>
                /// <param name="newAppender">
                /// The new appender.
                /// </param>
                /// <exception cref="ArgumentNullException">
                /// <paramref name="newAppender"/> is null.
                /// </exception>
                public void AddAppender(IAppender newAppender)
                {
                        if (newAppender == null)
                        {
                                throw new ArgumentNullException("newAppender");
                        }

                        lock (this)
                        {
                                if (_appenderAttachedImpl == null)
                                {
                                        _appenderAttachedImpl = new 
AppenderAttachedImpl();
                                }

                                _appenderAttachedImpl.AddAppender(newAppender);
                        }
                }

                /// <summary>
                ///     Close the appender.
                /// </summary>
                public void Close()
                {
                        // Remove all the attached appenders
                        lock (this)
                        {
                                if (!_closed)
                                {
                                        // Wait till all queued logevents are 
written to the connected appenders.
                                        _asyncLogTask.Wait();
                                        if (_appenderAttachedImpl != null)
                                        {
                                                
_appenderAttachedImpl.RemoveAllAppenders();
                                        }

                                        _closed = true;
                                }
                        }
                }

                /// <summary>
                /// Performs threshold checks and invokes filters before
                ///     delegating actual logging to the subclasses specific
                ///     Append method.
                /// </summary>
                /// <param name="loggingEvent">
                /// The event to event.
                /// </param>
                public void DoAppend(LoggingEvent loggingEvent)
                {
                        lock (this)
                        {
                                if (_closed)
                                {
                                        ErrorHandler.Error("Attempted to append 
to closed appender named [" + _name + "].");
                                        return;
                                }

                                // prevent re-entry
                                if (_recursiveGuard)
                                {
                                        return;
                                }

                                try
                                {
                                        _recursiveGuard = true;

                                        // Makes sure all properties are 
cached. To speed up the logging the flags could be set to some other value 
                                        // than the default FixFlags.All. 
However all Threadbased values such as user properties written to
                                        // ThreadContext.Properties[string] are 
lost, if not fixed.
                                        loggingEvent.Fix = _fixFlags;
                                        // This is where the magic happens. 
ContinueWith starts the AsyncAppend Method
                                        // as soon as the parent tasks has 
finished. Finished tasks are cleaned up by the GC.
                                        // This is because AttachToParent is 
not used. In .Net 4.5 this can be enforced with
                                        // 
TaskContinuationOptions.DenyChildAttach. Using ContinueWith also enures that the
                                        // logging Events maintain there order.
                                        _asyncLogTask = 
_asyncLogTask.ContinueWith(t => AsyncAppend(loggingEvent));
                                }
                                catch (Exception ex)
                                {
                                        ErrorHandler.Error("Failed in 
DoAppend", ex);
                                }
                                finally
                                {
                                        _recursiveGuard = false;
                                }
                        }
                }

                /// <summary>
                /// Performs threshold checks and invokes filters before
                ///     delegating actual logging to the subclasses specific
                ///     Append method.
                /// </summary>
                /// <param name="loggingEvents">
                /// The events to log.
                /// </param>
                public void DoAppend(LoggingEvent[] loggingEvents)
                {
                        lock (this)
                        {
                                if (_closed)
                                {
                                        ErrorHandler.Error("Attempted to append 
to closed appender named [" + _name + "].");
                                        return;
                                }

                                // prevent re-entry
                                if (_recursiveGuard)
                                {
                                        return;
                                }

                                try
                                {
                                        _recursiveGuard = true;
                                        // Makes sure all properties are 
cached. To speed up the logging the flags could be set to some other value 
                                        // than the default FixFlags.All. 
However all Threadbased values such as user properties written to
                                        // ThreadContext.Properties[string] are 
lost, if not fixed.
                                        Parallel.ForEach(loggingEvents, item => 
item.Fix = _fixFlags);
                                        // This is where the magic happens. 
ContinueWith starts the AsyncAppend Method
                                        // as soon as the parent tasks has 
finished. Finished tasks are cleaned up by the GC.
                                        // This is because AttachToParent is 
not used. In .Net 4.5 this can be enforced with
                                        // 
TaskContinuationOptions.DenyChildAttach. Using ContinueWith also enures that the
                                        // logging Events maintain there order.
                                        _asyncLogTask = 
_asyncLogTask.ContinueWith(t => AsyncAppend(loggingEvents));
                                }
                                catch (Exception ex)
                                {
                                        ErrorHandler.Error("Failed in Bulk 
DoAppend", ex);
                                }
                                finally
                                {
                                        _recursiveGuard = false;
                                }
                        }
                }

                /// <summary>
                /// Get the appender by name.
                /// </summary>
                /// <param name="name">
                /// The name of the appender.
                /// </param>
                /// <returns>
                /// The <see cref="IAppender"/> reference.
                /// </returns>
                public IAppender GetAppender(string name)
                {
                        lock (this)
                        {
                                if (_appenderAttachedImpl == null || name == 
null)
                                {
                                        return null;
                                }

                                return _appenderAttachedImpl.GetAppender(name);
                        }
                }

                /// <summary>
                ///     Remove all appenders.
                /// </summary>
                public void RemoveAllAppenders()
                {
                        lock (this)
                        {
                                // Wait till all queued logevents are written 
to the connected appenders.
                                _asyncLogTask.Wait();
                                if (_appenderAttachedImpl != null)
                                {
                                        
_appenderAttachedImpl.RemoveAllAppenders();
                                        _appenderAttachedImpl = null;
                                }
                        }
                }

                /// <summary>
                /// Remove appender method.
                /// </summary>
                /// <param name="appender">
                /// The appender to remove.
                /// </param>
                /// <returns>
                /// The <see cref="IAppender"/> reference.
                /// </returns>
                public IAppender RemoveAppender(IAppender appender)
                {
                        lock (this)
                        {
                                // Wait till all queued logevents are written 
to the connected appenders.
                                _asyncLogTask.Wait();
                                if (appender != null && _appenderAttachedImpl 
!= null)
                                {
                                        return 
_appenderAttachedImpl.RemoveAppender(appender);
                                }
                        }

                        return null;
                }

                /// <summary>
                /// Remove appender by name.
                /// </summary>
                /// <param name="name">
                /// The name of the appender.
                /// </param>
                /// <returns>
                /// The <see cref="IAppender"/> reference.
                /// </returns>
                public IAppender RemoveAppender(string name)
                {
                        lock (this)
                        {
                                // Wait till all queued logevents are written 
to the connected appenders.
                                _asyncLogTask.Wait();
                                if (name != null && _appenderAttachedImpl != 
null)
                                {
                                        return 
_appenderAttachedImpl.RemoveAppender(name);
                                }
                        }

                        return null;
                }

                #endregion

                #region Methods

                /// <summary>
                /// The async append.
                /// </summary>
                /// <param name="loggingEvent">
                /// The logging event.
                /// </param>
                private void AsyncAppend(LoggingEvent loggingEvent)
                {
                        if (_appenderAttachedImpl != null)
                        {
                                
_appenderAttachedImpl.AppendLoopOnAppenders(loggingEvent);
                        }
                }

                /// <summary>
                /// The async append.
                /// </summary>
                /// <param name="loggingEvents">
                /// The logging events.
                /// </param>
                private void AsyncAppend(LoggingEvent[] loggingEvents)
                {
                        if (_appenderAttachedImpl != null)
                        {
                                
_appenderAttachedImpl.AppendLoopOnAppenders(loggingEvents);
                        }
                }

                #endregion
        }
}
{code}

> AsyncAppender - better Implementation
> -------------------------------------
>
>                 Key: LOG4NET-407
>                 URL: https://issues.apache.org/jira/browse/LOG4NET-407
>             Project: Log4net
>          Issue Type: Improvement
>          Components: Appenders
>    Affects Versions: 4.0
>         Environment: .Net 4.0 and newer
>            Reporter: Michael Goldfinger
>            Priority: Minor
>
> I checked out the AsyncAppender and found some drawbacks.
> * logevents are not logged if the appender close
> * order of logevents got lost
> I created an new implementation that waits for all logevents to be computed 
> before close and maintains the order of the events. If the application 
> process got killed the logevents are lost too but in any other case the loss 
> of logevents could be prevented. The drawback of my implementation is that 
> the TLP is requred so .NET 2.0 is not supported.
> I could not find the place to contribute so I created this ticket. I hope 
> it's useful.



--
This message was sent by Atlassian JIRA
(v6.1#6144)

Reply via email to