Use Case: I have a form application in which I log events to a file/database and have a need to also raise those events to the application itself (for display purposes).  Think old school updating status bar with the same content that you will log to a file.The solution I put in place is a custom log4net class (since we're already using that for the actual file and databases logging).  This simple class just raises an event when the message payload of the caller.  This way I can use a single call to the log instead of multiple calls to different loggers.This is a working class, I'm just looking for input on pitfalls on the code or potential performance issues that might be found (and to also share the idea for others that need it).  I'm also wondering missing some best practices or something simple that I've overlooked.I the case of my requirements, this event may be called 30 times/sec across multiple different threads. MemoryQueueEventArgs isn't the actual final class name, it was another direction I was going to go, I just haven't renamed it yet. public class MemoryQueueEventArgs : EventArgs{  // Customize to your business requirements  public DateTime CreateDate { get; set; }  public string Message { get; set; }  public string Level { get; set; }  public string LoggerName { get; set; }  internal int Count { get; set; }}public class MemoryQueueAppender : AppenderSkeleton{  private object objectLock = new Object();  #region Public Methods  public static MemoryQueueAppender GetActiveAppender(Type type)  {    IAppender[] appenders = log4net.LogManager.GetLogger(type).Logger.Repository.GetAppenders();    return (appenders     .FirstOrDefault(a => a.GetType().Name == MemoryQueueAppender) as MemoryQueueAppender)    ;  }  protected event EventHandler<MemoryQueueEventArgs> _LogUpdate;  public event EventHandler<MemoryQueueEventArgs> LogUpdate  {    add    {      lock (objectLock)      {        _LogUpdate += value;      }    }    remove    {      lock (objectLock)      {        _LogUpdate -= value;      }    }  }  #endregion Public Methods  protected override void Append(log4net.Core.LoggingEvent loggingEvent)  {    MemoryQueueEventArgs mqea = _CreateMemoryQueueEventArgs(loggingEvent);    this.OnLogUpdate(mqea);  }  protected override void Append(log4net.Core.LoggingEvent[] loggingEvents)  {    base.Append(loggingEvents);  }  #region Private Methods  private MemoryQueueEventArgs _CreateMemoryQueueEventArgs(log4net.Core.LoggingEvent loggingEvent)  {    return new MemoryQueueEventArgs    {      CreateDate = DateTime.Now,      Level = loggingEvent.Level.Name,      LoggerName = loggingEvent.LoggerName,      Message = RenderLoggingEvent(loggingEvent),    };  }  protected virtual void OnLogUpdate(MemoryQueueEventArgs e)  {    EventHandler<MemoryQueueEventArgs> handler = _LogUpdate;    if (handler != null)    {      handler(this, e);    }  }  #endregion Private Methods}Calling Code:public TestDevelopment(){  MemoryQueueAppender appender = MemoryQueueAppender.GetActiveAppender(this.GetType());  appender.LogUpdate += appender_LogUpdateEvent;}void appender_LogUpdateEvent(object sender, MemoryQueueEventArgs e){  System.Diagnostics.Debug.WriteLine(string.Format([{0}] {1}, e.Level, e.Message));}
Custom log4net raising application-level events
c#;multithreading;.net;event handling;logging