我希望看到 Windows.Forms.RichTextBox 中显示的 log4net 条目。我正在考虑使用 MemoryAppender,但我不确定每次将其添加为事件时如何获取该条目。
最佳答案
log4net本质上是一个推模型,这一点并不明显(大多数人将方法调用与拉模型联系起来),但我们可以将其更改为大多数.NET开发人员更熟悉的另一种推模型(事件),并在其之上构建另一个推模型这使我们更容易订阅/取消订阅这些事件(可观察对象)。
您需要做的是创建 IAppender
interface 的实现并将对接口(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
实例。
首先,您将学习上面的类(class),并创建一个 IObservable<LogEventArgs>
使用 Observable.FromEvent
method :
// 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
的单个实例变得更容易一些。类(class)。 您的 代码没有理由必须这样做。这可以很容易地用 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));
然后,如果您想取消订阅(停止接收更新),您只需调用 Dispose
method在 IDisposable
上从对 Subscribe
的调用返回的实现.
使用 IObservable<T>
的一些优势这里是:
- 返回 UI 线程的回调由
ObserveOn
为您处理。方法。 - 您可以过滤/转换/等等。使用
System.Reactive.Linq
namespace 中的扩展方法的序列,特别是Observable
class 上的扩展方法允许使用 query expressions in C# . IObservable<T>
的扩展名实现允许您执行诸如缓冲(如果您方式一次收到太多消息)、滑动窗口等操作。- 您可以轻松地将此事件流与其他事件流结合起来。
关于.net - 如何使用 log4net 登录 RichTextBox?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13420929/