我希望看到Windows.Forms.RichTextBox中显示的log4net条目。我正在考虑使用MemoryAppender,但我不确定每次将其添加为事件时如何获取该条目。
答案 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
class的query expressions in C#上的扩展方法。IObservable<T>
实现的扩展允许您执行缓冲操作(如果您一次 way 太多消息),滑动窗口等等。