不具合が起きた時、不具合の原因を特定する情報として活用できるのがログです。
但しファイルアクセスはプログラム側からしてみればとても遅い処理なので、あまり影響されたくありません。
エラーによっては大量のログが出力されるかもしれません。
プログラム動作に影響を及ぼさずログを出力するには
1.通常のスレッドとは別で動いた方がいい
2.書き込みが遅ければ一時的に貯める場所があるといい
この2つがクリアできれば何とかなりそうです。
サンプルはこちら
使うもの
BlockingCollection
スレッドセーフなコレクションです。
TryAddメソッドで溜まっているコレクションの最後に追加します。
TryTakeメソッドで一番古いデータを取り出して、取り出したデータは消去します。
コレクションにデータがない時はデータが来るまで待ち状態となります。
CancellationTokenSource
TryTakeメソッドでコレクションが0以上になるのを待っている時、例外を発行し待ち状態を解除します。
CancellationTokenSourceのCancelメソッドが実行されると例外OperationCanceledExceptionが発行されTryTakeメソッドの待ち状態から抜けます。
コンストラクタ
シフトJISで例外を出さないおまじないとログを書くTaskの起動、2つ目の引数があれば即座にログを書き込みます。
2番目の引数を用意したのは、滅多な事ではログは書かないが必要な時はサッと書いて終わる動作をするために用意しました。
使い方としては下記のようになります。
こんな感じで例外が発生した時だけ内容を書き込んだら非同期で破棄できます。
破棄
破棄する時は念の為書き込まれてないログの存在を確認します。
ログが全て書き込まれてら
でBlockingCollectionのTryTakeメソッドに対してOperationCanceledException例外を発生させログを書き込むTaskを終了させます。
BlockingCollectionもCancellationTokenSourceも最後は破棄しないとメモリリークになりますのできちんと終了させて破棄しましょう。
ログ書き込みTaskを終了させないで破棄するとログ書き込み動作で不要な例外が発生します。
Dispose及びDisposeAsyncの全体像は
他のプログラムからログを書く
このクラスのインスタンスは生成させたままロクを連続的に記録する場合はMessageメソッドを実行します。
MessageメソッドでBlockingCollectionに日付を付加してログを追加します。
TryAddがコレクションの最後にログを追加するメソッドです。
ログをファイルに保存するく
実際のファイルへ書き込みとなります。
ファイル書き込みは遅い動作なので他のプログラムから来たログの内容を受け取る部分と実際のファイルにファイルへ書き込む部分を分けないとボトルネックになる可能性があります。
なので別Taskで溜まっているログの書き込みを行います。
最初に既存のログファイルの存在を確認して、ファイルが存在してなければ新しいファイルを作成します。
あとはひたすらTryTakeメソットでログが来るのを待ちます。
またログファイルを他のアプリで開いていてログが書き込めない状況だと例外が発生するので正常に書き込めるまでリトライします。
そしてOperationCanceledException例外が発生したら終了するためループから抜けます。
0 件のコメント:
コメントを投稿