ASP.NET Coreの非同期NLogで出力レベルの挙動を確認した

# 目次
- [はじめに](#はじめに)
- [開発環境](#開発環境)
- [実装手順](#実装手順)
    - [新しいプロジェクトの作成](###新しいプロジェクトの作成)
    - [NLogインストール](###NLogインストール)
    - [appsettings.jsonの変更](###appsettings.jsonの変更)
    - [NLogTestコントローラクラスの作成](###NLogTestコントローラクラスの作成)
    - [Program.csに追記](###Program.csに追記)
- [動作確認](#動作確認)
- [まとめ](#まとめ)

# はじめに
学習用としてASP.NET Core上でNLogを非同期処理で実装し、ログ出力レベルの挙動を確認しました。
そこでの手順や実行例を記載しました。
尚、設定ファイルでデフォルトの設定値をあえて明示する等、一部で冗長な表現があります。
これは学習を目的としている為で、実際の開発では削除するかどうか適宜ご判断願います。

# 開発環境

- Microsoft Visual Studio Community 2022
- .Net 8.0
- NLog 5.4.0
- NLog.Web.AspNetCore 5.4.0

# 実装手順
### 新しいプロジェクトの作成
VisualStudioを開き、新しいプロジェクトの作成をクリックします。
ASP.NET Core Web APIを選択します。
プロジェクト名とソリューション名をNLogTestと入力し、場所は任意のフォルダを選択し、次へをクリックします。
フレームワークを.NET 8.0を選択し、作成をクリックします。

### NLogインストール
Visual Studioで[ツール]>[NuGet パッケージ マネージャー]>[パッケージ マネージャー コンソール] を選択してコンソールを開きます。
以下のコマンドを実行します。

```Package Manager Console
Install-Package NLog.Web.AspNetCore 
Install-Package NLog 
```

### appsettings.jsonの変更
appsettings.jsonを下記に変更します。
internalLogFile、filenameは任意のファイルパスに設定します。

```appsettings.json
{
    "Logging": {
        "LogLevel": {
            "Default": "Information",
            "Microsoft.AspNetCore": "Warning"
        },
        "NLog": {
            "IncludeScopes": true,
            "RemoveLoggerFactoryFilter": true
        }
    },
    "NLog": {
        "autoReload": false,
        "throwConfigExceptions": true,
        "internalLogLevel": "Off",
        "internalLogFile": "${basedir}/Temp/nlog-${shortdate}-internal.log",
        "targets": {
            "async": true,
            "logFile": {
                "type": "File",
                "fileName": "${basedir}/Temp/nlog-${shortdate}.log",
                "layout": "${longdate} | ${level:uppercase=true} | ${message} ${exception:format=toString}"
            }
        },
        "rules": [
            {
                "logger": "*",
                "minLevel": "Info",
                "writeTo": "logfile"
            }
        ]
    }
}
```

### NLogTestコントローラクラスの作成
ソリューションエクスプローラのControllersフォルダで右クリック⇒追加⇒クラスを選択します。
左のタブでWebをクリック⇒APIコントローラー - 空を選択します。
ソリューションエクスプローラのValuesController.csをNLogTest.csにリネームします。
NLogTest.csのコードを下記に変更します。

```NLogTest.cs
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Http.HttpResults;
using Microsoft.AspNetCore.Mvc;
using NLog;
using NLog.Web;

namespace NLogTest.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class NLogTest : ControllerBase
    {
        private static NLog.Logger logger = NLog.LogManager.Setup()
                       .LoadConfigurationFromAppSettings()
                       .GetCurrentClassLogger();

        [HttpGet("Trace")]
        public IActionResult Trace()
        {
            logger.Trace("Trace accessed.");

            return Ok("Trace accepted.");
        }
        [HttpGet("Debug")]
        public IActionResult Debug()
        {
            logger.Debug("Debug accessed.");

            return Ok("Debug accepted.");
        }
        [HttpGet("Info")]
        public IActionResult Info()
        {
            logger.Info("Info accessed.");

            return Ok("Info accepted.");
        }
        [HttpGet("Warn")]
        public IActionResult Warn()
        {
            logger.Warn("Warn accessed.");

            return Ok("Warn accepted.");
        }
        [HttpGet("Error")]
        public IActionResult SimulateError()
        {
            try
            {
                throw new InvalidOperationException("SimulateError");
            }
            catch (Exception ex)
            {
                logger.Error(ex, "An error occurred.");
                return StatusCode(500, "An error occurred.");
            }
        }
        [HttpGet("Fatal")]
        public IActionResult SimulateFatalError()
        {
            try
            {
                throw new InvalidOperationException("SimulateFatalError");
            }
            catch (Exception ex)
            {
                logger.Fatal(ex, "A fatal error occurred.");
                return StatusCode(500, "A fatal error occurred.");
            }
        }
    }
}
```

### Program.csに追記
最下行のapp.Run();の前に以下を追加します。
ログが非同期で行われていても、アプリ終了時には全ログがフラッシュされます。

```Program.cs Add
// アプリケーション終了時に NLog のログをフラッシュするためのハンドラ登録（全体の停止時）
app.Lifetime.ApplicationStopping.Register(() =>
{
    // 非同期ログの場合も Shutdown() を呼び出すと内部で全ログがフラッシュされます。
    NLog.LogManager.Shutdown();
});
```

# 動作確認
プロジェクト作成時から導入されているSwaggerを使って確認します。
VisualStudio上で `ctrl + F5` を押下します。

![Fig1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4005878/88c3d1f2-4ebd-486b-ba09-b958c0e3f2e2.png)


`GET /api/NLogTest/Trace` をクリック⇒Try it outをクリック⇒Executeをクリック
以下、 `GET /api/NLogTest/Debug` 、 `GET /api/NLogTest/Info` 、 `GET /api/NLogTest/Warn` 、 `GET /api/NLogTest/Error` 、 `GET /api/NLogTest/Fatal` も同様に実行します。

尚、appsettings.jsonでNLogのrulesを以下の通り設定しています。

```appsettings.json
        "rules": [
            {
                "logger": "*",
                "minLevel": "Info",
                "writeTo": "logfile"
            }
        ]
```

ログファイルに出力する際の最低のレベルはInfoです。
ログレベルは以下の順でレベルが増大していきます。
`Trace` 、 `Debug` 、 `Info` 、 `Warn` 、 `Error` 、 `Fatal` 
コード上では6つのアクション全てでログを出力していますが、Info以上しかログ出力しないように設定している為、ログファイルにはInfo、Warn、Error、Fatalのみが出力されているはずです。

出力されたログファイルを確認します。

```nlog-[shortdate].log
2025-02-27 16:22:06.9887 | INFO | Info accessed. 
2025-02-27 16:22:11.1282 | WARN | Warn accessed. 
2025-02-27 16:22:14.8916 | ERROR | An error occurred. System.InvalidOperationException: SimulateError
   at NLogTest.Controllers.NLogTest.SimulateError() in ~\NLogTest\NLogTest\NLogTest\Controllers\NLogTest.cs:line 50
2025-02-27 16:22:18.5180 | FATAL | A fatal error occurred. System.InvalidOperationException: SimulateFatalError
   at NLogTest.Controllers.NLogTest.SimulateFatalError() in ~\NLogTest\NLogTest\NLogTest\Controllers\NLogTest.cs:line 63
```

想定通りTrace、Debugは出力されていない事が確認できました。

# まとめ
以上から、minLevelで指定した以上のレベルのログを出力する事が確認できました。
実際の開発現場を想定して非同期で実装していますが、今回以下の点が確認できていません。

- 非同期でログが出力されているのか。
- 非同期で未出力のログがアプリの終了時には全て書き込まれるのか。

次の課題は上記を確認出来る様にする予定です。