3

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

4

1 回答 1

7

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

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

让我们定义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接口

有了这个,我们可以创建一个IAppender. 请注意,您只需实现两种方法。两者中更重要的是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);
    }
}

一旦你有了这个,你可以以编程方式附加 appender。以编程方式执行此操作可能更容易,因为它有助于查找要将事件附加到的实例。但是,如果这是您的偏好,您可以遍历附加程序列表来找到它。

然后,只需将此实例中的事件连接到将写入RichTextBox.

一旦你有了这个,你可以很容易地把它变成一个IObservable<T>实现,并扁平化所有的LoggingEvent实例。

首先,您将学习上面的课程,并IObservable<LogEventArgs>使用以下Observable.FromEvent方法创建一个:

// 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扩展方法轻松完成:

// 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));

然后,如果您想取消订阅(停止接收更新),您只需在调用返回的实现上调用该Dispose方法即可。IDisposableSubscribe

使用IObservable<T>here 的一些优点是:

  • 回调到 UI 线程的编组由该ObserveOn方法为您处理。
  • 您可以过滤/转换/等。序列通过使用System.Reactive.Linq命名空间中的扩展方法,特别是允许在 C# 中使用查询表达式的Observable的扩展方法。
  • 实现的扩展IObservable<T>允许你做一些事情,比如缓冲(以防你一次收到太多消息)、滑动窗口等。
  • 您可以轻松地将此事件流与其他事件流结合起来。
于 2012-11-16T18:30:18.670 回答