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
。実装する必要があるメソッドは 2 つだけであることに注意してください。2 つのうちより重要なのは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>
により、バッファリング (一度に大量のメッセージを取得する場合)、ウィンドウのスライドなどを行うことができます。 - このイベント ストリームを他のイベント ストリームと簡単に組み合わせることができます。