.NET は、アプリケーションの動作の監視と問題の診断に役立つ、ILogger API を介した高パフォーマンスで構造化されたログ記録をサポートしています。 異なる宛先にログを書き込むには 、さまざまなログ プロバイダー を構成します。 基本的なログ プロバイダーは組み込まれており、多くのサードパーティ プロバイダーを使用できます。
概要
この最初の例は、基本を示すものですが、簡易的なコンソール アプリにのみ適しています。 このサンプル コンソール アプリは、次の NuGet パッケージに依存します。
- Microsoft.Extensions.Logging
- Microsoft.Extensions.Logging.Console (コンソール用のログ記録拡張機能)
次のセクションでは、スケール、パフォーマンス、構成、および一般的なプログラミング パターンを考慮してコードを改善する方法について説明します。
using Microsoft.Extensions.Logging;
using ILoggerFactory factory = LoggerFactory.Create(builder => builder.AddConsole());
ILogger logger = factory.CreateLogger("Program");
logger.LogInformation("Hello World! Logging is {Description}.", "fun");
上記の例の場合:
-
ILoggerFactory を作成します。
ILoggerFactoryには、ログ メッセージが送信される場所を決定するすべての構成が格納されます。 この場合は、ログ メッセージがコンソールに書き込まれるようにコンソール ログ プロバイダー を構成します。 - "Program" という名前のカテゴリを持つ ILogger を作成します。
カテゴリは、
stringオブジェクトによってログに記録される各メッセージに関連付けられているILoggerです。 ログを検索またはフィルター処理するときに、同じクラス (またはカテゴリ) からのログ メッセージをグループ化します。 -
LogInformation を呼び出して、
Informationレベルでメッセージをログに記録します。 ログ レベルは、ログに記録されたイベントの重大度を示し、重要度の低いログ メッセージを除外します。 ログ エントリには、メッセージ テンプレート"Hello World! Logging is {Description}."と、キーと値のペアDescription = funも含まれています。 キー名 (またはプレースホルダー) はテンプレート内の中かっこ内の単語から取得され、値は残りのメソッド引数から取得されます。
この例のこのプロジェクト ファイルには、次の 2 つの NuGet パッケージが含まれています。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Logging" Version="10.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="10.0.0" />
</ItemGroup>
</Project>
ヒント
ログのサンプル ソース コードはすべて、サンプル ブラウザーでダウンロードできます。 詳細については、コード サンプルの参照: .NET でのログ記録に関するページをご覧ください。
重要なアプリ以外でのログ記録
単純ではないシナリオでログを記録する場合は、前の例にこれらの変更を行うことを検討してください。
アプリケーションで 依存関係挿入 (DI) または ASP などのホストを使用している場合。NET の WebApplication または 汎用ホストでは、直接作成するのではなく、それぞれの DI コンテナーから
ILoggerFactoryオブジェクトとILoggerオブジェクトを使用します。 詳細については、DI およびホストとの統合に関する記事を参照してください。通常、コンパイル時のソース生成のログ記録は、
ILoggerのようなLogInformation拡張メソッドに代わる優れた方法です。 ログ ソース生成を行うと、パフォーマンスの向上と入力の強化に加え、メソッド全体にstring定数が分散しないようにすることができます。 トレードオフとして、この手法の使用には、コードがもう少し必要であるという点があります。
using Microsoft.Extensions.Logging;
internal partial class Program
{
static void Main(string[] args)
{
using ILoggerFactory factory = LoggerFactory.Create(builder => builder.AddConsole());
ILogger logger = factory.CreateLogger("Program");
LogStartupMessage(logger, "fun");
}
[LoggerMessage(Level = LogLevel.Information, Message = "Hello World! Logging is {Description}.")]
static partial void LogStartupMessage(ILogger logger, string description);
}
- ログ カテゴリ名には、ログ メッセージを作成するクラスの完全修飾名を使用することをお勧めします。 これにより、ログ メッセージを生成したコードに関連付け、ログをフィルター処理するときに適切なレベルの制御が提供されます。
CreateLogger は、この名前付けを簡単に実行できるようにする
Typeを受け入れます。
using Microsoft.Extensions.Logging;
internal class Program
{
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");
}
}
- コンソール ログを唯一の運用監視ソリューションとして使用しない場合は、使用予定のログ プロバイダーを追加します。 たとえば、 OpenTelemetry を使用して OTLP (OpenTelemetry プロトコル) 経由でログを送信します。
using Microsoft.Extensions.Logging;
using OpenTelemetry.Logs;
using ILoggerFactory factory = LoggerFactory.Create(builder =>
{
builder.AddOpenTelemetry(logging =>
{
logging.AddOtlpExporter();
});
});
ILogger logger = factory.CreateLogger("Program");
logger.LogInformation("Hello World! Logging is {Description}.", "fun");
ホストと依存性注入の統合
アプリケーションで 依存関係挿入 (DI) または ASP などのホストを使用している場合。NET の WebApplication または 汎用ホストでは、直接作成するのではなく、DI コンテナーから ILoggerFactory オブジェクトと ILogger オブジェクトを使用します。
DI から ILogger を取得する
次の例では、ASP.NET Minimal API を使用して、ホストされているアプリ内にある ILogger オブジェクトを取得します。
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddSingleton<ExampleHandler>();
var app = builder.Build();
var handler = app.Services.GetRequiredService<ExampleHandler>();
app.MapGet("/", handler.HandleRequest);
app.Run();
partial class ExampleHandler(ILogger<ExampleHandler> logger)
{
public string HandleRequest()
{
LogHandleRequest(logger);
return "Hello World";
}
[LoggerMessage(LogLevel.Information, "ExampleHandler.HandleRequest was called")]
public static partial void LogHandleRequest(ILogger logger);
}
上記の例の場合:
-
ExampleHandlerというシングルトン サービスを作成したとともに、ExampleHandler.HandleRequest関数の実行の受信 Web 要求をマップしました。 - 行 12 では、C# 12 で追加された機能である ExampleHandler のプライマリ コンストラクターを定義します。 以前のスタイルの C# コンストラクターの使用も同様に機能しますが、もう少し詳細です。
- コンストラクターを使用すると、
ILogger<ExampleHandler>型のパラメーターが定義されます。 ILogger<TCategoryName> は、ILogger から派生し、ILoggerオブジェクトに含まれるカテゴリを示します。 DI コンテナーでは、適切なカテゴリを持つILoggerを検索して、これをコンストラクター引数として指定します。 そのカテゴリを持つILoggerがまだ存在しない場合は、DI コンテナーによってサービス プロバイダーのILoggerFactoryから自動的に作成されます。 - コンストラクターで受け取った
loggerパラメーターは、HandleRequest関数のログ記録に使用されます。
ホストが提供するILoggerFactory
ホスト ビルダーを使用すると、既定の構成が初期化され、ホストのビルド時に、構成済みの ILoggerFactory オブジェクトがホストの DI コンテナーに追加されます。 ホストをビルドする前に、他のホスト上の HostApplicationBuilder.Logging、 WebApplicationBuilder.Logging、または同様の API を使用してログ構成を調整します。 ホストでは、 appsettings.json や環境変数などの既定の構成ソースからのログ記録構成も適用されます。 詳細については、「.NET での構成」を参照してください。
この例では、前述のものを展開して、ILoggerFactory によって指定された WebApplicationBuilder をカスタマイズします。 これにより、OpenTelemetry は、OTLP (OpenTelemetry プロトコル) でログを送信するログ プロバイダーとして追加されます。
var builder = WebApplication.CreateBuilder(args);
builder.Logging.AddOpenTelemetry(logging => logging.AddOtlpExporter());
builder.Services.AddSingleton<ExampleHandler>();
var app = builder.Build();
DI で ILoggerFactory を作成する
ホストなしで DI コンテナーを使用している場合は、AddLogging を使用し、ILoggerFactory を構成してコンテナーに追加します。
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
// Add services to the container including logging
var services = new ServiceCollection();
services.AddLogging(builder => builder.AddConsole());
services.AddSingleton<ExampleService>();
IServiceProvider serviceProvider = services.BuildServiceProvider();
// Get the ExampleService object from the container
ExampleService service = serviceProvider.GetRequiredService<ExampleService>();
// Do some pretend work
service.DoSomeWork(10, 20);
class ExampleService(ILogger<ExampleService> logger)
{
public void DoSomeWork(int x, int y)
{
logger.LogInformation("DoSomeWork was called. x={X}, y={Y}", x, y);
}
}
上記の例の場合:
- コンソールに書き込むように構成された
ILoggerFactoryを含む DI サービス コンテナーを作成しました - コンテナーにシングルトン
ExampleServiceを追加しました - DI コンテナーから
ExampleServiceのインスタンスを作成し、コンストラクター引数として使用するILogger<ExampleService>も自動的に作成しました。 -
ExampleService.DoSomeWork呼び出され、ILogger<ExampleService>を使用してコンソールにメッセージをログに記録しました。
ログの設定
コードまたは構成ファイルや環境変数などの外部ソースを使用して、ログ記録の構成を設定します。 外部構成を使用すると、アプリケーションをリビルドせずに変更できるため、可能な場合に役立ちます。 ただし、ログ プロバイダーの設定などの一部のタスクは、コードからのみ構成できます。
コードなしでログを構成する
ホストを使用するアプリの場合は、"Logging".jsonファイルの.{Environment}セクションでログ記録の構成が提供されるのが一般的です。 ホストを使用しないアプリの場合は、 外部構成ソースを明示的に設定 するか、代わりに コードで構成します 。
次の appsettings.Development.json ファイルは、.NET Worker サービス テンプレートによって生成されます。
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information"
}
}
}
上記の JSON では、次のように指定されています。
-
"Default"、"Microsoft"、"Microsoft.Hosting.Lifetime"ログ レベルのカテゴリが指定されています。 -
"Default"値は、特に指定されていないすべてのカテゴリに適用され、実質的にすべてのカテゴリのすべての既定値が"Information"されます。 カテゴリの値を指定して、この動作をオーバーライドします。 -
"Microsoft"カテゴリは、"Microsoft"で始まるすべてのカテゴリに適用されます。 -
"Microsoft"カテゴリでは、ログ レベルがWarning以上のログが記録されます。 -
"Microsoft.Hosting.Lifetime"カテゴリは"Microsoft"カテゴリよりも詳細なので、"Microsoft.Hosting.Lifetime"カテゴリではログ レベルが"Information"以上のログが記録されます。 - 特定のログ プロバイダーが指定されていないため、
LogLevelは、Windows EventLog を除くすべての有効なログ プロバイダーに適用されます。
Logging プロパティには LogLevel およびログ プロバイダーのプロパティを含めることができます。
LogLevel により、選択したカテゴリに対するログの最小レベルが指定されます。 上記の JSON では、Information と Warning のログ レベルが指定されています。
LogLevel はログの重大度を 0 - 6 の範囲で示します。
Trace = 0、Debug = 1、Information = 2、Warning = 3、Error = 4、Critical = 5、None = 6。
LogLevel を指定すると、指定したレベル以上のメッセージに対してログが有効になります。 上記の JSON では、Default カテゴリは Information 以上に対してのみログに記録されます。 たとえば、Information、Warning、Error、Critical のメッセージがログに記録されます。
LogLevel が指定されていない場合、ログは既定で Information レベルになります。 詳細については、「ログ レベル」を参照してください。
プロバイダー プロパティで LogLevel プロパティを指定できます。 プロバイダーの下の LogLevel によって、そのプロバイダーのログに記録するレベルが指定され、プロバイダー以外のログ設定がオーバーライドされます。 以下の appsettings.json ファイルについて考えます:
{
"Logging": {
"LogLevel": {
"Default": "Error",
"Microsoft": "Warning"
},
"Debug": {
"LogLevel": {
"Default": "Information",
"Microsoft.Hosting": "Trace"
}
},
"EventSource": {
"LogLevel": {
"Default": "Warning"
}
}
}
}
Logging.{ProviderName}.LogLevel の設定により Logging.LogLevel の設定がオーバーライドされます。 上記の JSON では、Debug プロバイダーの既定のログ レベルは Information に設定されています。
Logging:Debug:LogLevel:Default:Information
上記の設定により、Information 以外のすべての Logging:Debug: カテゴリに Microsoft.Hosting ログ レベルが指定されます。 特定のカテゴリが一覧表示されると、特定のカテゴリによって既定のカテゴリがオーバーライドされます。 上記の JSON では、Logging:Debug:LogLevel カテゴリ "Microsoft.Hosting" と "Default" によって、Logging:LogLevel の設定がオーバーライドされます。
次のいずれかの最小ログ レベルを指定します。
- 特定のプロバイダー: たとえば、
Logging:EventSource:LogLevel:Default:Information - 特定のカテゴリ: たとえば、
Logging:LogLevel:Microsoft:Warning - すべてのプロバイダーとすべてのカテゴリ:
Logging:LogLevel:Default:Warning
最小レベルを下回るログ は次の通りではありません。
- プロバイダーに渡される。
- ログに記録または表示される。
すべてのログを抑制するには、LogLevel.None を指定します。
LogLevel.None の値は、LogLevel.Critical (5) より大きい 6 です。
プロバイダーでログのスコープがサポートされている場合、IncludeScopes によってそれを有効にするかどうかが指定されます。 詳細については、ログ スコープのを参照してください。
次の appsettings.json ファイルには、すべての組み込みプロバイダー用の設定が含まれています。
{
"Logging": {
"LogLevel": {
"Default": "Error",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Warning"
},
"Debug": {
"LogLevel": {
"Default": "Information"
}
},
"Console": {
"IncludeScopes": true,
"LogLevel": {
"Microsoft.Extensions.Hosting": "Warning",
"Default": "Information"
}
},
"EventSource": {
"LogLevel": {
"Microsoft": "Information"
}
},
"EventLog": {
"LogLevel": {
"Microsoft": "Information"
}
},
"AzureAppServicesFile": {
"IncludeScopes": true,
"LogLevel": {
"Default": "Warning"
}
},
"AzureAppServicesBlob": {
"IncludeScopes": true,
"LogLevel": {
"Microsoft": "Information"
}
},
"ApplicationInsights": {
"LogLevel": {
"Default": "Information"
}
}
}
}
上記のサンプルについて:
- カテゴリとレベルは推奨値ではありません。 このサンプルでは、すべての既定のプロバイダーが表示されます。
-
Logging.{ProviderName}.LogLevelの設定によりLogging.LogLevelの設定がオーバーライドされます。 たとえば、Debug.LogLevel.DefaultのレベルによりLogLevel.Defaultのレベルがオーバーライドされます。 - 各プロバイダーの "エイリアス" が使用されています。 各プロバイダーは、完全修飾型名の代わりに構成で使用できる エイリアス を定義します。 組み込みプロバイダーのエイリアスは次のとおりです。
ConsoleDebugEventSourceEventLogAzureAppServicesFileAzureAppServicesBlobApplicationInsights
コマンド ライン、環境変数、およびその他の構成でログ レベルを設定する
構成プロバイダーのいずれかを使用してログ レベルを設定 します。 たとえば、Logging:LogLevel:Microsoftの値を持つInformationという名前の永続化された環境変数を作成します。
ログ レベルの値が代入された、永続化された環境変数を作成します。
:: Assigns the env var to the value
setx "Logging__LogLevel__Microsoft" "Information" /M
コマンド プロンプトの "新しい" インスタンスで、環境変数を読み取ります。
:: Prints the env var value
echo %Logging__LogLevel__Microsoft%
上記の環境設定は、環境に保持されます。 .NET Worker サービス テンプレートで作成されたアプリを使用して設定をテストするには、環境変数に代入した後、プロジェクト ディレクトリ内で dotnet run コマンドを使用します。
dotnet run
ヒント
環境変数を設定した後、統合開発環境 (IDE) を再起動して、新しく追加した環境変数を使用できることを確認します。
Azure App Service の、 ページで > を選択します。 Azure App Service アプリケーションの設定は:
- 保存時に暗号化され、暗号化されたチャネルで送信されます。
- 環境変数として公開されます。
環境変数を使用して .NET 構成値を設定する方法の詳細については、環境変数に関する記事を参照してください。
コードを使用してログ記録を構成する
コードにログを構成するには、ILoggingBuilder API を使用します。 さまざまな場所からアクセスできます。
-
ILoggerFactoryを直接作成する場合は、LoggerFactory.Create に構成します。 - ホストなしで DI を使用する場合は、LoggingServiceCollectionExtensions.AddLogging に構成します。
- ホストを使用する場合は、HostApplicationBuilder.Logging、WebApplicationBuilder.Logging、またはその他のホスト固有の API を使用して構成します。
この例では、コンソール ログ プロバイダーといくつかのフィルターを設定しています。
using Microsoft.Extensions.Logging;
using var loggerFactory = LoggerFactory.Create(static builder =>
{
builder
.AddFilter("Microsoft", LogLevel.Warning)
.AddFilter("System", LogLevel.Warning)
.AddFilter("LoggingConsoleApp.Program", LogLevel.Debug)
.AddConsole();
});
ILogger logger = loggerFactory.CreateLogger<Program>();
logger.LogDebug("Hello {Target}", "Everyone");
前の例では、AddFilterさまざまなカテゴリで有効になっているログ レベルを調整します。
AddConsole はコンソール ログ プロバイダーを追加します。 既定では、Debug の重大度のログは有効になっていませんが、構成によってフィルターが調整されたため、デバッグ メッセージ "Hello Everyone" がコンソールに表示されます。
フィルター規則を適用する方法
ILogger<TCategoryName> オブジェクトを作成すると、ILoggerFactory オブジェクトによって、そのロガーに適用するプロバイダーごとに 1 つの規則が選択されます。
ILogger インスタンスは、選択したルールに基づいて、書き込むすべてのメッセージをフィルター処理します。 使用できる規則から、各プロバイダーとカテゴリのペアごとに最も明確な規則が選択されます。
特定のカテゴリに ILogger が作成されるときに、各プロバイダーに次のアルゴリズムが使用されます。
- プロバイダーとそのエイリアスと一致するすべての規則が選択されます。 一致が見つからない場合は、空のプロバイダーですべての規則が選択されます。
- 前の手順の結果、最も長いカテゴリのプレフィックスが一致する規則が選択されます。 一致が見つからない場合は、カテゴリを指定しないすべての規則が選択されます。
- 複数の規則が選択されている場合は、最後の 1 つが使用されます。
- 規則が選択されていない場合は、LoggingBuilderExtensions.SetMinimumLevel(ILoggingBuilder, LogLevel) を使用して最小ログ記録レベルを指定します。
ログのカテゴリ
ILogger オブジェクトが作成されるときに、"カテゴリ" が指定されます。 このカテゴリは、ILogger のインスタンスによって作成される各ログ メッセージと共に含められます。 カテゴリ文字列は任意ですが、慣習的には完全修飾クラス名を使用します。 たとえば、サービスが次のオブジェクトように定義されているアプリケーションでは、カテゴリは "Example.DefaultService" のようになります。
namespace Example
{
public class DefaultService : IService
{
private readonly ILogger<DefaultService> _logger;
public DefaultService(ILogger<DefaultService> logger) =>
_logger = logger;
// ...
}
}
さらなる分類が必要な場合、慣習的には、次のように完全修飾クラス名にサブカテゴリを追加することで階層的な名前を使用し、LoggerFactory.CreateLogger を使用してカテゴリを明示的に指定します。
namespace Example
{
public class DefaultService : IService
{
private readonly ILogger _logger;
public DefaultService(ILoggerFactory loggerFactory) =>
_logger = loggerFactory.CreateLogger("Example.DefaultService.CustomCategory");
// ...
}
}
固定名で CreateLogger を呼び出すことは、イベントをカテゴリ別に整理できるように、複数のクラス/型で使用する場合に便利です。
ILogger<T> は、CreateLogger の完全修飾型名を使用した T の呼び出しと同じです。
ログ レベル
次の表に、LogLevel 値、便利な Log{LogLevel} 拡張メソッド、推奨される使用法を示します。
| LogLevel | [値] | 方法 | 説明 |
|---|---|---|---|
| トレース | 0 | LogTrace | 最も詳細なメッセージが含まれます。 これらのメッセージには、機密性の高いアプリ データが含まれている場合があります。 これらのメッセージは既定で無効になっているため、運用環境では有効に "しないでください"。 |
| デバッグ | 1 | LogDebug | デバッグと開発用。 量が多いため、運用環境では慎重に使用してください。 |
| 情報 | 2 | LogInformation | アプリの一般的なフローを追跡します。 長期的な価値があるかもしれません。 |
| 警告 | 3 | LogWarning | 異常なイベントや予期しないイベント用。 通常、アプリが失敗する原因にならないエラーや条件が含まれます。 |
| エラー | 4 | LogError | 処理できないエラーと例外の場合。 これらのメッセージは、アプリ全体のエラーではなく、現在の操作や要求における失敗を示します。 |
| 重大 | 5 | LogCritical | 即時の注意が必要なエラーの場合。 例: データ損失のシナリオ、ディスク領域不足。 |
| なし | 6 | メッセージを書き込む必要がないことを指定します。 |
前の表では、重大度が最も低い方から高い方に LogLevel が一覧表示されています。
Log メソッドの最初のパラメーター LogLevel は、ログの重大度を示します。 ほとんどの開発者は、Log(LogLevel, ...) を呼び出すのではなく、Log{LogLevel} 拡張メソッドを呼び出します。
Log{LogLevel} 拡張メソッドによって、Log メソッドが呼び出され、LogLevel が指定されます。 たとえば、次の 2 つのログ呼び出しは、機能的に同等で、同じログが生成されます。
public void LogDetails()
{
var logMessage = "Details for log.";
_logger.Log(LogLevel.Information, AppLogEvents.Details, logMessage);
_logger.LogInformation(AppLogEvents.Details, logMessage);
}
AppLogEvents.Details はイベント ID であり、暗黙的に Int32 定数値によって表されます。
AppLogEvents はさまざまな名前付き識別子定数を公開するクラスであり、ログ イベント ID セクションに表示されます。
Information および Warning ログを作成するコードを次に示します。
public async Task<T> GetAsync<T>(string id)
{
_logger.LogInformation(AppLogEvents.Read, "Reading value for {Id}", id);
var result = await _repository.GetAsync(id);
if (result is null)
{
_logger.LogWarning(AppLogEvents.ReadNotFound, "GetAsync({Id}) not found", id);
}
return result;
}
上記のコードでは、最初の Log{LogLevel} パラメーター ( AppLogEvents.Read) は Log イベント ID です。 2 つ目のパラメーターは、他のメソッド パラメーターによって提供される引数値のプレースホルダーを含むメッセージ テンプレートです。 メソッド パラメーターについては、この記事の後半のメッセージ テンプレートに関するセクションで説明します。
適切なログ レベルを構成し、適切な Log{LogLevel} メソッドを呼び出して、特定のストレージ メディアに書き込むログ出力の量を制御します。 次に例を示します。
- 運用環境:
-
TraceまたはDebugのレベルでログを記録すると、詳細なログ メッセージが大量に生成されます。 コストを制御し、データ ストレージの上限を超えないようにするには、TraceおよびDebugのレベルのメッセージを、大容量の低コストのデータ ストアに記録します。TraceとDebugを特定のカテゴリに制限することを検討してください。 -
WarningからCriticalのレベルでログを記録しても、ログ メッセージはほとんど生成されません。- 通常、コストとストレージの制限は考慮されません。
- ログの数が少ないほど、データ ストアをより柔軟に選択できるようになります。
-
- 開発中:
-
Warningに設定します。 - トラブルシューティングの際に
TraceまたはDebugのメッセージを追加します。 出力を制限するには、調査中のカテゴリに対してのみTraceまたはDebugを設定します。
-
次の JSON は Logging:Console:LogLevel:Microsoft:Information を設定します。
{
"Logging": {
"LogLevel": {
"Microsoft": "Warning"
},
"Console": {
"LogLevel": {
"Microsoft": "Information"
}
}
}
}
ログ イベント ID
各ログでは、"イベント識別子" を指定できます。EventId は、Id および省略可能な Name 読み取り専用プロパティを持つ構造体です。 サンプル ソース コードでは、AppLogEvents クラスを使用してイベント ID を定義しています。
using Microsoft.Extensions.Logging;
internal static class AppLogEvents
{
internal static EventId Create = new(1000, "Created");
internal static EventId Read = new(1001, "Read");
internal static EventId Update = new(1002, "Updated");
internal static EventId Delete = new(1003, "Deleted");
// These are also valid EventId instances, as there's
// an implicit conversion from int to an EventId
internal const int Details = 3000;
internal const int Error = 3001;
internal static EventId ReadNotFound = 4000;
internal static EventId UpdateNotFound = 4001;
// ...
}
ヒント
int を EventId に変更する方法については、「EventId.Implicit(Int32 to EventId) 演算子」を参照してください。
イベント ID によって一連のイベントが関連付けられます。 たとえば、リポジトリからの値の読み取りに関連するすべてのログを 1001 にすることができます。
ログ プロバイダーは、イベント ID を ID フィールド、ログ メッセージ、またはまったくログに記録しない可能性があります。 Debug プロバイダーでイベント ID が表示されることはありません。 Console プロバイダーでは、カテゴリの後のブラケット内にイベント ID が表示されます。
info: Example.DefaultService.GetAsync[1001]
Reading value for a1b2c3
warn: Example.DefaultService.GetAsync[4000]
GetAsync(a1b2c3) not found
一部のログ プロバイダーでは、イベント ID がフィールドに格納されます。これにより、ID に対するフィルター処理が可能になります。
ログ メッセージ テンプレート
各ログ API では、メッセージ テンプレートが使用されます。 メッセージ テンプレートには、指定される引数のためのプレースホルダーを含めることができます。 プレースホルダーには、数値ではなく名前を使用します。 プレースホルダーの名前ではなく、プレースホルダーの順序によって、値の指定に使用されるパラメーターが決まります。 次のコードでは、パラメーター名がメッセージ テンプレート内のシーケンスの外にあります。
string p1 = "param1";
string p2 = "param2";
_logger.LogInformation("Parameter values: {p2}, {p1}", p1, p2);
上記のコードでは、シーケンス内のパラメーター値を含むログ メッセージが作成されます。
Parameter values: param1, param2
注
1 つのメッセージ テンプレート内で複数のプレースホルダーを使用するときは注意してください。序数基準になります。 名前は、引数をプレースホルダーに合わせるために使用されません。
この方法により、ログ プロバイダーは セマンティック ログまたは構造化ログを実装できます。 書式設定されたメッセージ テンプレートだけでなく、引数自体がログ システムに渡されます。 これにより、ログ プロバイダーはフィールドとしてパラメーター値を格納することができます。 次のロガー メソッドについて考えてみます。
_logger.LogInformation("Getting item {Id} at {RunTime}", id, DateTime.Now);
たとえば、Azure Table Storage にログを記録する場合:
- 各 Azure Table エンティティには、
IDプロパティとRunTimeプロパティを含めることができます。 - プロパティを持つテーブルによって、ログに記録されたデータに対するクエリが簡略化されます。 たとえば、クエリによって、特定の
RunTimeの範囲内のすべてのログを検索できます。テキスト メッセージから時間を解析する必要はありません。
ログ メッセージ テンプレートの書式設定
ログ メッセージ テンプレートでは、プレースホルダーの書式設定がサポートされています。 テンプレートでは、指定された型引数に 任意の有効な形式 を指定できます。 たとえば、次の Information ロガー メッセージ テンプレートについて考えてみます。
_logger.LogInformation("Logged on {PlaceHolderName:MMMM dd, yyyy}", DateTimeOffset.UtcNow);
// Logged on January 06, 2022
前の例では、DateTimeOffset インスタンスは、ロガー メッセージ テンプレートの PlaceHolderName に対応する型になります。 値は序数基準であり、この名前には何でも指定できます。
MMMM dd, yyyy 形式は DateTimeOffset 型で有効です。
DateTime および DateTimeOffset の詳細については、「カスタム日時形式文字列」をご覧ください。
例
次の例では、{} プレースホルダー構文を使用してメッセージ テンプレートを書式設定する方法が示されます。 さらに、{} プレースホルダー構文をエスケープする例がその出力と共に示されます。 最後に、テンプレート プレースホルダーを使用した文字列補間も示されます。
logger.LogInformation("Number: {Number}", 1); // Number: 1
logger.LogInformation("{{Number}}: {Number}", 3); // {Number}: 3
logger.LogInformation($"{{{{Number}}}}: {{Number}}", 5); // {Number}: 5
ヒント
- ほとんどの場合、ログ記録時には、常にログ メッセージ テンプレートの書式設定を使用する必要があります。 文字列補間を使用すると、パフォーマンスに問題が発生することがあります。
- コード分析のルール「CA2254: テンプレートは静的な式にする必要があります」は、ログ メッセージに適切な書式設定が使用されていない場所について警告するのに役立ちます。
例外をログに記録する
ロガー メソッドには、例外パラメーターを受け取るオーバーロードがあります。
public void Test(string id)
{
try
{
if (id is "none")
{
throw new Exception("Default Id detected.");
}
}
catch (Exception ex)
{
_logger.LogWarning(
AppLogEvents.Error, ex,
"Failed to process iteration: {Id}", id);
}
}
例外ログはプロバイダー固有です。
既定のログ レベル
既定のログ レベルが設定されていない場合、既定のログ レベルの値は Information。
たとえば、次のようなワーカー サービス アプリについて考えてみます。
- .NET Worker テンプレートを使用して作成された。
- appsettings.json と appsettings.Development.json が削除または名前が変更された。
上記の設定で、プライバシー ページまたはホーム ページに移動すると、カテゴリ名に Trace が含まれる多くの Debug、Information、Microsoft のメッセージが生成されます。
次のコードは、既定のログ レベルが構成で設定されていない場合に既定のログ レベルを設定します。
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Logging.SetMinimumLevel(LogLevel.Warning);
using IHost host = builder.Build();
await host.RunAsync();
フィルター関数
フィルター関数は、構成またはコードによって規則が割り当てられていないすべてのプロバイダーとカテゴリに対して呼び出されます。
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Logging.AddFilter((provider, category, logLevel) =>
{
return provider.Contains("ConsoleLoggerProvider")
&& (category.Contains("Example") || category.Contains("Microsoft"))
&& logLevel >= LogLevel.Information;
});
using IHost host = builder.Build();
await host.RunAsync();
上記のコードでは、カテゴリに Example または Microsoft が含まれ、ログ レベルが Information 以上の場合にコンソール ログが表示されます。
ログのスコープ
"スコープ" では、論理操作のセットをグループ化します。 このグループ化では、セットの一部として作成された各ログに同じデータをアタッチできます。 たとえば、トランザクション処理の一部として作成されるすべてのログに、トランザクション ID を含めることができます。
スコープは:
- IDisposable メソッドによって返される BeginScope 型です。
- 破棄されるまでそのまま有効です。
次のプロバイダーではスコープがサポートされています。
ロガーの呼び出しを using ブロックでラップすることによって、スコープを使用します。
public async Task<T> GetAsync<T>(string id)
{
T result;
var transactionId = Guid.NewGuid().ToString();
using (_logger.BeginScope(new List<KeyValuePair<string, object>>
{
new KeyValuePair<string, object>("TransactionId", transactionId),
}))
{
_logger.LogInformation(
AppLogEvents.Read, "Reading value for {Id}", id);
var result = await _repository.GetAsync(id);
if (result is null)
{
_logger.LogWarning(
AppLogEvents.ReadNotFound, "GetAsync({Id}) not found", id);
}
}
return result;
}
次の JSON では、Console プロバイダーのスコープを有効にしています。
{
"Logging": {
"Debug": {
"LogLevel": {
"Default": "Information"
}
},
"Console": {
"IncludeScopes": true,
"LogLevel": {
"Microsoft": "Warning",
"Default": "Information"
}
},
"LogLevel": {
"Default": "Debug"
}
}
}
次のコードでは、Console プロバイダーのスコープを有効にしています。
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Logging.ClearProviders();
builder.Logging.AddSimpleConsole(options => options.IncludeScopes = true);
using IHost host = builder.Build();
await host.RunAsync();
Main でログを作成する
次のコードでは、ホストを構築した後に DI から Main インスタンスを取得することによって ILogger でログを記録します。
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using IHost host = Host.CreateApplicationBuilder(args).Build();
var logger = host.Services.GetRequiredService<ILogger<Program>>();
logger.LogInformation("Host created.");
await host.RunAsync();
上記のコードは、次の 2 つの NuGet パッケージに依存しています。
プロジェクト ファイルは次のようになります。
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net7.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="7.0.1" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
</ItemGroup>
</Project>
非同期でないロガー メソッド
ログ記録は高速に実行され、非同期コードのパフォーマンス コストを下回る必要があります。 ログ データ ストアが低速の場合は、そこへ直接書き込まないでください。 まずログ メッセージを高速なストアに書き込んでから、後で低速なストアに移動することを検討してください。 たとえば、SQL Server にログを記録する場合、Log メソッドは同期しているため、Log メソッドで直接それを行わないでください。 代わりに、ログ メッセージをインメモリ キューに同期的に追加し、バックグラウンド ワーカーにキューからメッセージをプルさせて、SQL Server にデータをプッシュする非同期処理を実行させます。
実行中のアプリのログ レベルを変更する
ログ API には、アプリの実行中にログ レベルを変更するシナリオは含まれていません。 ただし、一部の構成プロバイダーは構成を再読み込みでき、ログ記録の構成にすぐに影響します。 たとえば、ファイル構成プロバイダーでは、既定でログ構成が再度読み込まれます。 アプリの実行中にコード内の構成を変更した場合、アプリは IConfigurationRoot.Reload を呼び出してアプリのログ構成を更新できます。
NuGet パッケージ
ILogger<TCategoryName> と ILoggerFactory のインターフェイスと実装は、ほとんどの .NET SDK に暗黙的なパッケージ参照として含まれています。 暗黙的に参照されない場合でも、次の NuGet パッケージで明示的に使用することができます。
- インターフェイスは、Microsoft.Extensions.Logging.Abstractions にあります。
- 既定の実装は、Microsoft.Extensions.Logging にあります。
どの .NET SDK に暗黙的なパッケージ参照が含まれるかの詳細については、.NET SDK: 暗黙的な名前空間へのテーブルに関する記事を参照してください。
関連項目
- .NET でのログ プロバイダー
- .NET にカスタム ログ プロバイダーを実装する
- コンソール ログの書式設定
- .NET での高パフォーマンスのログ
- .NET ライブラリ作成者のためのログのガイダンス
- ログのバグは、github.com/dotnet/runtime/ リポジトリに作成する必要があります。
.NET