我想查看 Windows.Forms.RichTextBox 中显示的 log4net 条目。我正在考虑使用 MemoryAppender,但我不确定每次将其添加为事件时如何获取该条目。
1 回答
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
方法即可。IDisposable
Subscribe
使用IObservable<T>
here 的一些优点是:
- 回调到 UI 线程的编组由该
ObserveOn
方法为您处理。 - 您可以过滤/转换/等。序列通过使用
System.Reactive.Linq
命名空间中的扩展方法,特别是允许在 C# 中使用查询表达式的Observable
类的扩展方法。 - 实现的扩展
IObservable<T>
允许你做一些事情,比如缓冲(以防你一次收到太多消息)、滑动窗口等。 - 您可以轻松地将此事件流与其他事件流结合起来。