はなちるのマイノート

Unityをメインとした技術ブログ。自分らしくまったりやっていきたいと思いますー!

【C#】MS製のLoggingライブラリである「Microsoft.Extensions.Logging」の基礎的な使い方

はじめに

前回messagetemplates-csharpというMessageTemplateを扱えるようにするライブラリを紹介しました。

www.hanachiru-blog.com

今回はMS製のMicrosoft.Extensions.LoggingというLoggingライブラリについて紹介したいと思います。(Microsoft.Extensions.LoggingMessageTemplateに対応しています)

github.com

利用している様子

概要

Microsoft.Extensions.LoggingMS製のLoggingパッケージです。構造化ロギングに対応しており、コンソールへのテキスト・Jsonなどへの出力を行うことができます。

.NET は、アプリケーションの動作の監視と問題の診断に役立つ、ILogger API を介した高パフォーマンスで構造化されたログ記録をサポートしています。 さまざまなログ プロバイダーを構成することで、ログをさまざまな宛先に書き込むことができます。 基本的なログ プロバイダーは組み込みであり、多くのサードパーティ プロバイダーも利用できます。

C# でのログ記録 - .NET | Microsoft Learn

環境

Rider2023.3.3
Microsoft.Extensions.Logging v8.0.0
Microsoft.Extensions.Logging.Console v8.0.0

インストール

NuGetからMicrosoft.Extensions.Loggingをインストールします。

RiderでNuGetからインストールしている様子

また使用したいプロバイダー(出力先)ごとにパッケージを追加する必要があります。今回はコンソールに出力するサンプルのみ紹介するので以下のパッケージを追加でインストールします。

  • Microsoft.Extensions.Logging.Console

Getting Started

まずはシンプルなコードから使い方を学んでいきます。

using Microsoft.Extensions.Logging;

namespace SampleConsole;

public static class Program
{
    public static void Main(string[] args)
    {
        using ILoggerFactory factory = LoggerFactory.Create(builder => builder.AddConsole());
        ILogger logger = factory.CreateLogger("Program");
        logger.LogInformation("Hello World! Logging is {Description}.", "fun");
    }
}

LoggerFactory.Create

ILoggerFactoryのインスタンスを生成します。引数のconfigureを利用してログメッセージが送信される出力先(プロバイダー)が設定します。

// LogLevel.Information以上のメッセージをコンソールに出力するよう設定する
using ILoggerFactory factory = LoggerFactory.Create(builder => builder.SetMinimumLevel(LogLevel.Information).AddConsole());

またSetMinimumLevel(LogLevel.〇〇)のように指定したLogLovel以上のログのみ出力するような設定ができます。LogLevelについては後述します。

Unity LoggingLoggerConfigを用いたLogger生成に少し似ていますね。(Unityの場合はFactoryを確か生成しませんが)

www.hanachiru-blog.com

ILoggerFactory.CreateLogger

ILoggerのインスタンスを生成します。

ILogger logger = factory.CreateLogger("Program");

引数のcategoryNameによってメッセージのカテゴリ分けがされます。例えばConsoleの場合はProgramに設定するとinfo: Program[0]みたいな文字列が先頭に表示されるようになります。ログを検索またはフィルター処理するときに、同じクラス (またはカテゴリ) のログ メッセージをグループ化するために使用されます。

// 出力例
info: Program[0]
      Hello World! Logging is fun.
info: Program[0]
      Hello World! Logging is sad.
info: Program[0]
      Hello World! Logging is joy.

ILogger.Log〇〇

Microsoft.Extensions.Loggingには以下のLogLevelが設定されています。詳細はこちらから。

public enum LogLevel
{
    Trace,
    Debug,
    Information,
    Warning,
    Error,
    Critical,
    None,
}

これらに対応したログメソッドがあります。(Noneはないです)

using ILoggerFactory factory = LoggerFactory.Create(builder => builder.SetMinimumLevel(LogLevel.Trace).AddConsole());
ILogger logger = factory.CreateLogger("Program");
logger.LogTrace("Hello World! Logging is {Description}.", "fun");
logger.LogDebug("Hello World! Logging is {Description}.", "fun");
logger.LogInformation("Hello World! Logging is {Description}.", "fun");
logger.LogWarning("Hello World! Logging is {Description}.", "fun");
logger.LogError("Hello World! Logging is {Description}.", "fun");
logger.LogCritical("Hello World! Logging is {Description}.", "fun");
コンソールに出力されている例

MessageTemplateについて

ILogger.Log〇〇メソッドはMessageTemplateに対応しています。これによって構造化ロギングを実現しています。

// ILogger.LogInformation
public static void LogInformation(this ILogger logger, string? message, params object?[] args)
// Jsonとして出力したサンプル
using ILoggerFactory factory =
    LoggerFactory.Create(builder => builder.AddJsonConsole());
ILogger logger = factory.CreateLogger("Program");

// コンソールへの出力 : {"EventId":0,"LogLevel":"Information","Category":"Program","Message":"User alice logged in from 123.45.67.89","State":{"Message":"User alice logged in from 123.45.67.89","username":"alice","ip_address":"123.45.67.89","{OriginalFormat}":"User {username} logged in from {ip_address}"}}
logger.LogInformation("User {username} logged in from {ip_address}", "alice", "123.45.67.89");

MessageTemplateの詳細は以下に記載したので気になる方はチェックしてみてください。
www.hanachiru-blog.com

ログのカテゴリ名(categoryName)について

最初の例では文字列で指定していましたが、ジェネリクスを用いて型名を指定した方が推奨されています。
C# でのログ記録 - .NET | Microsoft Learn

public class Program
{
    public static void Main(string[] args)
    {
        using ILoggerFactory factory =
            LoggerFactory.Create(builder => builder.SetMinimumLevel(LogLevel.Trace).AddJsonConsole());

        // 型を指定する
        ILogger logger = factory.CreateLogger<Program>();

        logger.LogInformation("User {username} logged in from {ip_address}", "alice", "123.45.67.89");
    }
}

EventIdについて

ILogger.Log〇〇メソッドの引数にイベント識別子(EventId)を指定することができます。

public static void Main(string[] args)
{
    using ILoggerFactory factory = LoggerFactory.Create(builder => builder.AddJsonConsole());
    ILogger logger = factory.CreateLogger<Program>();
        
    // Text : info: SampleConsole.Program[1]  User alice logged in from 123.45.67.89
    // Json : {"EventId":1,"LogLevel":"Information","Category":"SampleConsole.Program","Message":"User alice logged in from 123.45.67.89","State":{"Message":"User alice logged in from 123.45.67.89","username":"alice","ip_address":"123.45.67.89","{OriginalFormat}":"User {username} logged in from {ip_address}"}}
    logger.LogInformation(new EventId(1, "sample"), "User {username} logged in from {ip_address}", "alice", "123.45.67.89");
}

categoryNameに近い概念ですが、Logメソッドに対して割り振ることができます。
C# でのログ記録 - .NET | Microsoft Learn

さいごに

OpenTelemetry .NETを用いることでログの出力先をOpenTelemetryに対応できるそうです。この辺結構気になっているので、また後ほど記事を書きたいと思っています。

github.com