かずきのBlog@hatena

すきな言語は C# + XAML の組み合わせ。Azure Functions も好き。最近は Go 言語勉強中。日本マイクロソフトで働いていますが、ここに書いていることは個人的なメモなので会社の公式見解ではありません。

Enterprise Library入門 その3 「Logging Application Block」

Logging Application Block

ここではLogging Application Blockについて説明します。業務アプリケーションにおいて、ログは必須の構成要素です。地味な機能ですが、テスト時やリリース後の障害発生時の問題切り分けの重要な情報になります。また適切なログを出力して監視することで、障害の予兆を検知したり様々な利用方法があります。
.NETでは、System.Diagnostics.Traceを使ったログ出力機能が標準で備わっていますが、業務システムで必須の機能セットを備えているかと言われると力不足と言わざるを得ません。例えば、ファイルサイズが1000KBになったタイミングで今のログファイルを別名で退避するといったありがちな機能がありません。また、障害発生時に必要となるプロセスIDやアプリケーションドメイン名、コンピュータ名などの様々な情報を出力するような機能も用意されていません。
Enterprise LibraryのLogging Application Blockは、業務アプリケーション開発にひつような上記のSystem.Diagnostics.Traceに不足している機能セットを提供します。

ログ出力機能の設定

ここでは、Fluent APIで設定できる設定項目について説明します。完全な設定内容は、リファレンスの下記のページから参照してください。ここでは代表的ないくつかを実際の記述例をもとに説明します。
Microsoft.Practices.EnterpriseLibrary.Common.Configuration.Fluent Namespace

Fluent APIの難点は、APIドキュメントを見ただけでは何が出来るのかわからないという点にあると思います。そのため、インテリセンスとサンプルを見て記述のコツをつかむのが大事です。以下に、単純にログに出力するケースの設定コード例を示します。

01. var builder = new ConfigurationSourceBuilder();
02. builder.ConfigureLogging()
03.     // 名前を付けてログの定義を開始
04.     .LogToCategoryNamed("General")
05.         // WithOptionsで追加オプション
06.         // ここではGeneralをデフォルトのカテゴリとして設定
07.         .WithOptions.SetAsDefaultCategory()
08.         // フラットファイルに出力ファイル名はdefault.log
09.         .SendTo.FlatFile("FlatFileListener").ToFile("default.log")
10.         // フィルタリング(警告以上を表示する)
11.         .Filter(SourceLevels.Warning)
12.         // ログのフォーマットを指定
13.         .FormatWith(new FormatterBuilder()
14.             // フォーマッタの名前を指定
15.             .TextFormatterNamed("LogFormatter")
16.             // フォーマットを指定
17.             .UsingTemplate("{timestamp(local:yyyy/MM/dd HH:mm:ss.fff)}:  {severity}: {message}"));
  1. ConfigureLoggingメソッド
    1. ログの構成を開始するメソッド
  2. LogToCategoryNamedメソッド
    1. 名前を付けてカテゴリを作成するメソッド。後続のメソッドチェインでカテゴリの設定を行う。再度、LogToCategoryNamedメソッドが呼ばれるまで、このメソッドで作成したカテゴリに対する設定になる。
  3. WithOptionsプロパティ
    1. ログにオプションを追加する。ここではデフォルトのカテゴリとして指定している。
  4. SendToプロパティ
    1. ログの出力先を指定する。ここではフラットファイルを指定している。このほかにもイベントログやMSMQやDatabaseなど様々な出力先に対応している。
  5. Filterメソッド
    1. ログのフィルタリングを行う条件を指定する。ここでは警告以上のログを出力するようにしている。
  6. FormatWithメソッド
    1. ログのフォーマットを指定するメソッド。フォーマットはFormatBuilderというヘルパークラスがあるので、それを使って作成する。フォーマットに指定できる名前は、LogEntryクラスのプロパティ名と大体同じ。(LogEntryクラス

上記の構成をした状態で下記のようなログを出力するコードを記載します。

01. var l = EnterpriseLibraryContainer.Current.GetInstance<LogWriter>();
02. // Verbose〜Criticalまでのログを出力
03. l.Write(new LogEntry { Message = "VerboseMessage", Severity = TraceEventType.Verbose});
04. l.Write(new LogEntry { Message = "InformationMessage", Severity = TraceEventType.Information});
05. l.Write(new LogEntry { Message = "WarningMessage", Severity = TraceEventType.Warning});
06. l.Write(new LogEntry { Message = "ErrorMessage", Severity = TraceEventType.Error});
07. l.Write(new LogEntry { Message = "CriticalMessage", Severity = TraceEventType.Critical});

Enterprise LibraryのコンテナからLogWriterを取得してログを出力します。LogWriterのWriteメソッドを使用してログを出力できます。ここでは、LogEntryというログに出力する情報を表すクラスを渡すオーバーロードを指定してVerbose〜Criticalまでのログを出力しています。このプログラムを実行すると下記のログが出力されます。

----------------------------------------
2012/04/09 22:41:24.096:  Warning: WarningMessage
----------------------------------------
----------------------------------------
2012/04/09 22:41:24.110:  Error: ErrorMessage
----------------------------------------
----------------------------------------
2012/04/09 22:41:24.110:  Critical: CriticalMessage
----------------------------------------

Filterメソッドで指定したとおり警告(Warning)以上のログしか出力していないことが確認できます。

ローリングの設定

先ほどのサンプルでは単純なフラットファイルでのログの出力だったので、ここではファイルを一定の条件でローリングする設定方法について説明します。

01. var builder = new ConfigurationSourceBuilder();
02. builder.ConfigureLogging()
03.     // 名前を付けてログの定義を開始
04.     .LogToCategoryNamed("General")
05.         // WithOptionsで追加オプション
06.         // ここではGeneralをデフォルトのカテゴリとして設定
07.         .WithOptions.SetAsDefaultCategory()
08.         // フラットファイルに出力ファイル名はdefault.log
09.         .SendTo.FlatFile("FlatFileListener").ToFile("default.log")
10.         // フィルタリング(警告以上を表示する)
11.         .Filter(SourceLevels.Warning)
12.         // ログのフォーマットを指定
13.         .FormatWith(new FormatterBuilder()
14.             // フォーマッタの名前を指定
15.             .TextFormatterNamed("LogFormatter")
16.             // フォーマットを指定
17.             .UsingTemplate("{timestamp(local:yyyy/MM/dd HH:mm:ss.fff)}:  {severity}: {message}"))
18.     // Rollingという名前でログの定義を開始
19.     .LogToCategoryNamed("Rolling")
20.         // SendTo.RollingFileで一定の条件を満たしたらローリング
21.         .SendTo.RollingFile("RollingFileListener")
22.             // 1000KBでローリング
23.             .RollAfterSize(1000)
24.             // 1分間隔でローリング
25.             .RollEvery(RollInterval.Minute)
26.             // ローリングしたファイルにタイムスタンプをつける
27.             .UseTimeStampPattern("yyyyMMddHHmmssfff")
28.             // 10世代管理
29.             .CleanUpArchivedFilesWhenMoreThan(10)
30.             // ファイル名はrolling.log
31.             .ToFile("rolling.log");

上記コードの19行目からが、ローリングの設定です。SendToでRollingFileメソッドを使うことでローリングの設定が出来ます。その後に、ローリングするときの条件(サイズや時間)にローリングしたときのファイル名の命名規約や何世代までログを管理するか指定します。上記のような設定で、ログを大量に出力した結果を以下に示します。

1000KBのファイルが10世代、管理されていることが確認できます。今回のコードではローリングするログの設定をカテゴリ名”Rolling”で作成したため、ログ出力の際にこのカテゴリ名を指定する必要があります。(指定しない場合はデフォルトに設定しているGeneralが使用されます。ログ出力部分のコードは下記のようになります。

01. var l = EnterpriseLibraryContainer.Current.GetInstance<LogWriter>();
02. l.Write("ログメッセージ", "Rolling");
03. l.Write(new LogEntry { Message = "sample message", Categories = { "Rolling" } });

LogWriterのWriteメソッドの第二引数でカテゴリ名を設定します。LogEntryを使用する場合はCategoriesプロパティにカテゴリを文字列の配列で渡します。Categoriesプロパティに複数のカテゴリを指定することで、一度に複数個所にログを出力することも可能です。

イベントログへの出力

最後にイベントログへの出力例を説明します。イベントログに出力するにはSendToのあとにEventLogメソッドを呼び出します。そして、UsingEventLogSourceメソッドで何処に出力するか指定します。コード例を以下に示します。

01. var builder = new ConfigurationSourceBuilder();
02. builder.ConfigureLogging()
03.     // 名前を付けてログの定義を開始
04.     .LogToCategoryNamed("General")
05.         // WithOptionsで追加オプション
06.         // ここではGeneralをデフォルトのカテゴリとして設定
07.         .WithOptions.SetAsDefaultCategory()
08.         // フラットファイルに出力ファイル名はdefault.log
09.         .SendTo.FlatFile("FlatFileListener").ToFile("default.log")
10.         // フィルタリング(警告以上を表示する)
11.         .Filter(SourceLevels.Warning)
12.         // ログのフォーマットを指定
13.         .FormatWith(new FormatterBuilder()
14.             // フォーマッタの名前を指定
15.             .TextFormatterNamed("LogFormatter")
16.             // フォーマットを指定
17.             .UsingTemplate("{timestamp(local:yyyy/MM/dd HH:mm:ss.fff)}:  {severity}: {message}"))
18.     // Rollingという名前でログの定義を開始
19.     .LogToCategoryNamed("Rolling")
20.         // SendTo.RollingFileで一定の条件を満たしたらローリング
21.         .SendTo.RollingFile("RollingFileListener")
22.             // 1000KBでローリング
23.             .RollAfterSize(1000)
24.             // 1分間隔でローリング
25.             .RollEvery(RollInterval.Minute)
26.             // ローリングしたファイルにタイムスタンプをつける
27.             .UseTimeStampPattern("yyyyMMddHHmmssfff")
28.             // 10世代管理
29.             .CleanUpArchivedFilesWhenMoreThan(10)
30.             // ファイル名はrolling.log
31.             .ToFile("rolling.log")
32.     // EventLogという名前でログの定義を開始
33.     .LogToCategoryNamed("EventLog")
34.         // EventLogに送信するEventLogListener
35.         .SendTo.EventLog("EventLogListener")
36.         // ソースはApplication
37.         .UsingEventLogSource("Application");

33行目からが追加したイベントログの定義になります。コメントにあるようにApplicationのログに出力するように設定しています。この状態で下記のようなコードを書くとイベントログにログが出力されます。

01. var l = EnterpriseLibraryContainer.Current.GetInstance<LogWriter>();
02. l.Write("EventLogMessage", "EventLog", 0, 0, TraceEventType.Information);

プログラムを実行して、イベントログを確認するとログが出力されていることがわかります。

その他の機能

この他にもLogging Application BlockにはデータベースやWCFへのログの出力やメールなど一般的な用途に使えそうなログ出力の機能が提供されています。また、LogWriterクラスのIsLoggingEnabledメソッドなどを使うことで不要な時はログを出力しないといったコードも作成することが可能です。

まとめ

以上でLogging Application Blockの説明は終わりです。Logging Application Blockが、かなり多機能なログ出力の機能を持っていることが確認できたと思います。地味ながら業務システムに必須のログ出力機能を提供しています。また、構成を変更することでアプリケーションロジックのコードには手を入れることなくログの出力先やログのフィルタリングも行えます。特に採用するログ出力ライブラリが決まっていないときの選択肢の1つとして検討してみても良いと思います。