如何使用log4net登录RichTextBox?

时间:2012-11-16 16:51:46

标签: .net c#-4.0 log4net

我希望看到Windows.Forms.RichTextBox中显示的log4net条目。我正在考虑使用MemoryAppender,但我不确定每次将其添加为事件时如何获取该条目。

1 个答案:

答案 0 :(得分:7)

log4net本质上是推送模型,这并不明显(大多数人将方法调用与拉模型相关联),但我们可以将其更改为大多数.NET开发人员更熟悉的另一个推送模型(事件)并在此基础上构建另一个推送模型,这使我们更容易订阅/取消订阅这些事件(可观察的)。

您需要做的是创建IAppender interface的实现,并将对接口实现的调用转换为事件。

让我们定义您将用于指示事件发生的EventArgs类:

public class LogEventArgs : EventArgs
{
    public LogEventArgs(IEnumerable<LoggingEvent> loggingEvents)
    {
        // Validate parameters.
        if (loggingEvents == null) 
            throw new ArgumentNullException("loggingEvents");

        // Assign values.
        LoggingEvents = loggingEvents;
    }

    // Poor-man's immutability.
    public IEnumerable<LoggingEvent> LoggingEvents { get; private set; }
}

值得注意的是,这会暴露一系列LoggingEvent个实例,因为我们也希望支持IBulkAppender interface

在此之后,我们可以创建IAppender的实现。请注意,您必须实现two methods。这两个中更重要的是DoAppend,您可以将调用转换为事件:

public class EventAppender : AppenderSkeleton
{
    // Note, you will probably have to override other things here.

    // The lock for the event.
    private readonly object _eventLock = new object();

    // The backing field for the event.
    private EventHandler<LogEventArgs> _loggedEventHandlers;

    // Add and remove methods.
    public event Logged
    {
        add { lock(_eventLock) _loggedEventHandlers += value; }
        remove { lock(_eventLock) _loggedEventHandlers -= value; }
    }

    // Singular case.
    protected override void Append(LoggingEvent loggingEvent)
    {
        // Validate parameters.
        if (loggingEvent == null) 
            throw new ArgumentNullException("loggingEvent");

        // Call the override that processes these in bulk.
        Append(new [] { loggingEvent });
    }

    // Multiple case.
    protected override void Append(LoggingEvent[] loggingEvents)
    {
        // Validate parameters.
        if (loggingEvents == null)
            throw new ArgumentNullException("loggingEvents");

        // The event handlers.
        EventHandler<LogEventArgs> handlers;

        // Get the handlers.
        lock (_eventLock) handlers = _loggedEventHandlers;

        // Fire if not null.
        if (handlers != null) handlers(new LogEventArgs(loggingEvents);
    }
}

有了这个,你可以programmatically append the appender。以编程方式执行此操作可能更容易,因为它有助于查找将事件附加到的实例。但是,you can walk the list of appenders to find it如果这是您的偏好。

然后,只需将事件从此实例连接到将写入RichTextBox的处理程序。

完成此操作后,您可以轻松将其转换为IObservable<T>实施,并展平所有LoggingEvent个实例。

首先,您将使用上面的课程,并使用Observable.FromEvent method创建IObservable<LogEventArgs>

// The appender that was added programmatically.
EventAppender appender = ...;

// Get the observable of LogEventArgs first.
IObservable<LogEventArgs> logEventObservable = 
    Observable.FromEvent<LogEventArgs>(
        a => appender.Logged += a, a => appender.Logged -= a);

但是,此时,我们希望处理LoggingEvent类的各个实例变得更容易一些。没有理由你的代码必须这样做。这可以通过SelectMany extension method

轻松完成
// The observable of the individual LoggingEvent instances.
IObservable<LoggingEvent> loggingEvents = logEventObservable.
    SelectMany(e => e.LoggingEvents);

然后,您可以轻松订阅IObservable<LoggingEvent>实例,以便在新事件进入时发布到UI线程:

// The RichTextBox.
RichTextBox rtb = ...;

// This code goes where you want to start subscribing.  This needs to be
// called on the UI thread, because you need the proper
// SynchronizationContext:
IObservable<LoggingEvent> synchronized = loggingEvents.ObserveOn(
    SynchronizationContext.Current);

// Subscribe to the event.  Store the result of this.
IDisposable unsubscribe = synchronized.
    // If you need to do anything fancier, then use the
    // LoggingEvent represented in e and update the
    // RichTextBox accordingly.
    Subscribe(e => rtb.AppendText(e.RenderedMessage));

然后,如果您想取消订阅(停止接收更新),只需致电Subscribe来电后返回的Dispose method实施内容IDisposable

在这里使用IObservable<T>的一些优点是:

  • 通过ObserveOn方法为您处理回调到UI线程的编组。
  • 您可以过滤/转换/等。序列使用System.Reactive.Linq namespace中的扩展方法,尤其是允许使用Observable classquery expressions in C#上的扩展方法。
  • IObservable<T>实现的扩展允许您执行缓冲操作(如果您一次 way 太多消息),滑动窗口等等。
  • 您可以轻松地将此事件流与其他事件流结合使用。