I think it's much easier to discuss something concrete, so I've put up an incomplete version of the AsyncAppenderSkeleton I envisage on the following branch:
https://github.com/JJoe2/log4net/tree/wip/AsyncAppender I would appreciate it if you could review this and let me know if you're happy to go in this direction before I invest more time in it. Some notes on this implementation: 1. I've created a new class AppenderBase into which I've extracted all the code from AppenderSkeleton that I want to reuse in AsyncAppenderSkeleton. AppenderSkeleton derives from this class. The purpose here is twofold (a) to avoid duplication of code between AppenderSkeleton and AsyncAppenderSkeleton and (b) to make it easier to review the AsyncAppenderSkeleton class. 2. The queue implementation is a separate class that implements IAppenderQueue which I haven't implemented yet. I plan to write three classes (a) a base AppenderQueueSkeleton class with common stuff to be shared by all queues (default error / retry handling etc); (b) a class that uses a simple lossy in-memory FIFO queue (System.Collections.Queue); (c) a class that uses MSMQ so the queue can be made persistent. 3. The class operates in synchronous mode by default, and is intended to be functionally identical to AppenderSkeleton. 4. When in asynchronous mode, by default LoggingEvents are queued after being fixed, and the Append method of the derived class is called as logging events are dequeued. 5. Alternatively, a derived class can override FormatLoggingEvent to format the logging event before it is queued. In this case, a new method AppendFormattedEvents of the derived class is called instead of Append as events are dequeued. This can improve performance, because it avoids the need to fix properties of LoggingEvent. 6. The IOptionHandler.ActivateOptions implementation has error handling. If the appender is not configured successfully, it will ignore any logging events it receives. 7. I haven't done any work on the locking yet, but the intention is to hold a lock while Append is called, so that the derived class can rely on the fact that Apppend will never be called concurrently by multiple threads.