ASP.NET Coreにカスタムロギングを追加しよう！

# 目次
- [はじめに](#はじめに)
- [環境](#環境)
- [実装](#実装)
- [結果](#結果)
- [終わりに](#終わりに)

# はじめに
この記事は、ASP.NET Coreのランタイムではない場所、例えばリクエストのログなどをカスタマイズしたい方のための記事です。
この記事では、以下の目標が達成できます。
- ログを自分好みにカスタマイズできます
- ログをカラフルにすることができます
- ログを綺麗に成型することができます

# 環境
- [.NET 8.0.11 Sdk](https://dotnet.microsoft.com/ja-jp/download/dotnet/8.0)
- VSCode
セットアップについては、こちらをご覧ください -> [Qiita](https://qiita.com/KaRU3_dev/items/7e92b7dc45f99391eaf6#%E5%89%8D%E6%8F%90%E3%82%BB%E3%83%83%E3%83%88%E3%82%A2%E3%83%83%E3%83%97)

# 実装
では、実装していこう
#### 1. プロジェクトの作成
```powershell
E:\Project> dotnet new webapi -o Example
E:\Project> dotnet new sln
E:\Project> dotnet sln add Example
```

#### 2. 統一用にロガーライブラリを作成
```powershell
E:\Project> dotnet new classlib -o Example.Logging
E:\Project> dotnet sln add Example.Logging
```

#### 3. ライブラリをプロジェクトに追加
```powershell
E:\Project> cd Example
E:\Project\Example> dotnet add reference ..\Example.Logging\Example.Logging.csproj
E:\Project\Example> cd ..\Example.Logging
E:\Project\Example.Logging> dotnet add package Microsoft.Extensions.Logging -Version 8.0.1
```

### 4. ILoggerProviderの実装
```cs:Example.Logging\LoggerProvider.cs
namespace Example.Logging;

public class LoggerProvider : ILoggerProvider
{
    public ILogger CreateLogger(string categoryName)
    {
        return new LogSystem(categoryName);
    }

    public void Dispose()
    {
        GC.SuppressFinalize(this);
    }
}
```
### 5. ILoggerの実装
```csharp:Example.Logging\LogSystem.cs
namespace Example.Logging;

public class LogSystem : ILogger
{
    public IDisposable? BeginScope<TState>(TState state) where TState : notnull
    {
        // オプション、スコープ処理を必要であれば実装
        throw new NotImplementedException();
    }

    public bool IsEnabled(LogLevel logLevel)
    {
        // オプション、ログレベルのチェックなどを必要であれば実装
        throw new NotImplementedException();
    }

    public void Log<TState>(
        LogLevel logLevel,
        EventId eventId,
        TState state,
        Exception? exception,
        Func<TState,Exception?, string> formatter
    )
    {
        // カスタムロギング
        var now = DateTime.Now.ToString("yyyy/MM/dd HH:mm:ss");
        var levelStr = LogLevelToString(logLevel);
        var strechLevelStr = LogStringFormat(levelStr);
        var message = formatter(state, exception);

        Console.ForegroundColor = ConsoleColor.Cyan;
        Console.Write($"[");
        Console.ResetColor();
        Console.Write(now);
        Console.ForegroundColor = ConsoleColor.Cyan;
        Console.Write($"] ");

        Console.Write($"[");
        Console.ResetColor();
        Console.Write(strechLevelStr);
        Console.ForegroundColor = ConsoleColor.Cyan;
        Console.Write($"] ");

        Console.ResetColor();
        Console.WriteLine(message);
    }

    private string LogLevelToString(LogLevel level) => loglevel switch
    {
        LogLevel.Trace => "trace",
        LogLevel.Debug => "debug",
        LogLevel.Information => "information",
        LogLevel.Warning => "warning",
        LogLevel.Error => "error",
        _ => "none"
    };

    private string LogStringFormat(string levelStr)
    {
        var upperdLevel = loglevel.ToUpper();

        var length = 11;

        if ( length > MaxLogLevelStringLength ) {
            upperdLevel = upperdLevel.Substring(0, MaxLogLevelStringLength);
        }

        if ( length < MaxLogLevelStringLength ) {
            upperdLevel = upperdLevel.PadRight(MaxLogLevelStringLength, ' ');
        }

        return upperdLevel;
    }
}
```
#### 6. ASP.NET Coreのサービスに注入
```Example\Startup.cs
namespace Example;

public class Startup( IConfiguration configuration )
{
    public void ConfigureServices( IServiceCollection services )
    {
        services.AddLogging(builder => {
            builder.ClearProviders();
            builder.AddProvider(new LoggerProvider());
        });

        ...
    }

    public void Configure( IApplicationBuilder builder, IHostEnvironment env )
    {
        ...
    }
}
```

# 結果
```powershell
E:\Project> dotnet run --project Example
Hosting environment: Development
Content root path: E:\Project\Example
Now listening on: https://[::]:2525
Application started. Press Ctrl+C to shut down.
[2024/11/15 22:03:03] [INFORMATION] {Log message}
[2024/11/15 22:03:05] [INFORMATION] {Log message}
Application is shutting down...
```


# 終わりに
`builder.ClearProviders();`してないと元からあるやつもログを出力するから、必要であればやってね！
コメント待ってます！またね！