[.NET][備忘録]Microsoft.Extensions.Loggingのログの仕組みを理解する

# はじめに

これまでログ出力の一般的なライブラリを使ったこともなく、仕組みがよくわかっていませんでした。今回は、Microsoft.Extensions.Loggingを利用するにあたり、調べて理解に時間がかかったことを中心にわかったことをまとめます。

## ILogger

ILoggerは実際にログを出力する役割のクラスです。
コンソールに出力する用のロガー、ファイルに出力する用のロガー等用途に応じてロガーを使用します。

## ILoggerProvider

ILoggerのインスタンスの生成を行うクラスです。
.NETではコンソールやWindowsのイベントログ等にログ出力するためのILoggerを生成できるプロバイダがあります。詳しくは[こちら](https://learn.microsoft.com/ja-jp/dotnet/core/extensions/logging-providers#built-in-logging-providers)。
ファイル出力を行うプロバイダは.NETで提供されていません。3rd Party製のライブラリを使用するのが簡単です。詳しくは[こちら](https://learn.microsoft.com/ja-jp/dotnet/core/extensions/logging-providers#third-party-logging-providers)。
ILoggerおよびILoggerProviderを実装すれば独自の出力先のロガーを作成することもできます。

## LoggerFactory.CreateとILoggerFactory.CreateLogger

最初メソッド名が似ていて混乱しましたが、LoggerFactory.CreateはLoggerFactoryのインスタンスを生成するための静的メソッドです。
LoggerFactoryにはILoggerProviderを登録できます。

ILoggerFactory.CreateLoggerはILoggerのインスタンスを生成するメソッドです。
LoggerFactory.CreateLoggerで生成したILoggerは内部的にLoggerFactoryに登録されたすべてのILoggerProviderで生成したILoggerを持っています。
ILogger.Log（ILoggerのログを出力するメソッド）を呼び出すと、すべてのプロバイダに対してログを出力できます。
ILoggerFactoryは個別プロバイダのラッパー的な役割がありますね。

例えば以下のように複数のプロバイダをILoggerFactoryに登録しておくと、ILogger.Logを呼び出したタイミングですべてのプロバイダで生成したILogger.Logメソッドが呼ばれ、すべての出力先にログを出力できます。

```cs

using Microsoft.Extensions.Logging;
using NLog.Extensions.Logging;

using ILoggerFactory loggerFactory =
	LoggerFactory.Create(builder =>
		{
            // .NETで提供されているプロバイダ
            // ILoggerFactory.AddProvider()にプロバイダのインスタンスを渡してプロバイダを登録できるが
            // ILoggingBuilderというプロバイダのインスタンス生成部分を隠蔽してプロバイダを登録できる仕組みがある
			builder.AddSimpleConsole();

            // ファイル出力をするための3rd Party製のプロバイダ（NLog）の登録
            // ※このままでは何も設定が無いので利用できません（後述します）
			builder.AddNLog();
		}
	);

// ジェネリックを指定してログのカテゴリを指定できます。詳しくは以下。
// https://learn.microsoft.com/en-us/dotnet/core/extensions/logging?tabs=command-line#log-category
ILogger<Program> logger = loggerFactory.CreateLogger<Program>();

// ILoggerのI/FにはLogメソッドしかありませんが、便利な拡張メソッドがあります。
// 以下はログレベルをInfomationにしてログを出力できる拡張メソッドを使用しています。
// コンソールとファイルの両方にログを出力しています。
logger.LogInformation("Hello World!");

```

## (番外編)NLogの設定

ファイル出力をするためのロガーとしてNLogを使用しましたのでそこで行った設定を記載します。
[こちら](https://github.com/NLog/NLog/wiki/File-target)のWikiを参考に色々なオプションを設定すると良いと思います。
ファイルの他にもコンソール、DB、メールなどNLogのプロバイダに設定を追加すれば様々な出力先にログを出力できます。
今回はコード上で設定を行っていますが、NLog.configやAppSettings.jsonでも設定ができます。

```cs

using Microsoft.Extensions.Logging;
using NLog.Extensions.Logging;

using ILoggerFactory loggerFactory =
	LoggerFactory.Create(builder =>
		{
                // NLogでファイル出力するための設定
		        var config = new NLog.Config.LoggingConfiguration();

				// ログのファイルの出力先を設定
                var logFile = new FileTarget("logfile")
                {
	                FileName = "{出力先のログファイルのパス}",
                    // デフォルトのレイアウトです
                    Layout = "${longdate}|${level:uppercase=true}|${logger}|${message:withexception=false}\r\n"
                };
                
                // ログの出力レベルを設定
                config.AddRule(NLog.LogLevel.Info, NLog.LogLevel.Fatal, logFile);

                // ログのファイル出力用にNLogのプロバイダを登録
 		        builder.AddNLog(config);
		}
	);

```


# さいごに

Microsoft.Extensions.Loggingについて調べたことをまとめました。
ILoggerProviderとILoggerFactoryの関係の理解に少し詰まったので、そこが整理できてよかったです。