C# Logger完全ガイド|ログ出力の基本・設定方法・おすすめライブラリとエラー解決策

はじめに

C#でアプリケーションを開発していると、「エラーの原因が分からない」「本番環境で何が起きたか追跡できない」「ユーザー操作やバッチ処理の履歴を確認したい」といった場面が必ず出てきます。そこで重要になるのが、C# Loggerを使ったログ出力です。

Loggerを正しく使うと、アプリケーションの状態、処理の流れ、例外の詳細、外部APIやデータベースとのやり取りを後から確認できます。特にASP.NET Coreや.NET 5以降の開発では、Microsoft.Extensions.Loggingを中心とした標準的なログ基盤が用意されており、SerilogやNLogなどのライブラリと組み合わせて柔軟にログを管理できます。Microsoft公式ドキュメントでも、ASP.NET CoreはILogger APIによる構造化ログをサポートし、アプリの動作監視や問題診断に利用できると説明されています。

この記事では、C# Loggerの基本、標準Loggerの仕組み、実装方法、設定方法、Serilog・NLog・log4netの比較、よくあるエラーの解決策、本番運用を意識したベストプラクティスまでをまとめて解説します。

1. C# Loggerとは?ログ出力の基本と必要性

1-1. Loggerの役割とは

Loggerとは、アプリケーションの処理状況やエラー情報を記録するための仕組みです。C#におけるLoggerは、単に文字列を画面に表示するだけでなく、ログレベル、カテゴリ、タイムスタンプ、例外情報、ユーザーID、リクエストIDなどを含めて、後から分析しやすい形で記録できます。

たとえば、Web APIでエラーが発生した場合、Loggerを使って「どのエンドポイントで」「どの入力値に対して」「どの例外が発生したか」を残しておけば、障害調査が格段に楽になります。ログは、開発中のデバッグだけでなく、本番環境の監視、障害対応、セキュリティ調査、性能分析にも使われます。

1-2. Console.WriteLineとの違い

Console.WriteLineは、コンソールに文字列を出力するだけのシンプルな方法です。一方、Loggerはログレベルの制御、出力先の切り替え、例外情報の記録、構造化ログ、環境別設定などに対応できます。

たとえば、Console.WriteLineでは「開発環境ではDebugログを出すが、本番環境ではWarning以上だけ出す」といった制御が難しくなります。Loggerであれば、appsettings.jsonや環境変数によってログ出力の条件を変更できます。Microsoftのログ機能では、ログレベルによって出力の詳細度を制御でき、カテゴリごとに最小ログレベルを指定できます。

1-3. C#でログ出力が必要になる場面

C#でLoggerが必要になる代表的な場面は、次のようなケースです。

場面ログに残す内容
Web APIリクエスト内容、レスポンス結果、ステータスコード、処理時間
バッチ処理開始時刻、終了時刻、処理件数、失敗理由
データベース処理SQL実行エラー、接続エラー、対象ID
外部API連携リクエスト先、レスポンスコード、タイムアウト
認証・認可ログイン成功・失敗、権限エラー
例外発生時例外メッセージ、スタックトレース、関連パラメータ

ログがないシステムでは、本番障害が起きたときに「何が起きたか」を推測するしかありません。逆に、適切なログがあれば、原因の切り分けや再発防止策の検討がしやすくなります。

1-4. ログに記録すべき情報・記録すべきでない情報

ログに記録すべき情報は、障害調査や処理追跡に役立つ情報です。たとえば、処理名、対象データのID、リクエストID、ユーザーID、処理件数、実行時間、例外情報などが挙げられます。

一方で、パスワード、アクセストークン、クレジットカード番号、個人番号、秘密鍵、認証Cookieなどはログに記録してはいけません。メールアドレス、電話番号、住所、氏名などの個人情報も、必要性がない限り避けるべきです。どうしても識別情報が必要な場合は、マスキングやハッシュ化を検討します。

悪い例は次のとおりです。

C#
logger.LogInformation("Login request: User={User}, Password={Password}", userName, password);

改善例は次のとおりです。

C#
logger.LogInformation("Login requested. UserId={UserId}", userId);

1-5. ログレベルの基本:Trace・Debug・Information・Warning・Error・Critical

C# Loggerでは、ログの重要度に応じてログレベルを使い分けます。Microsoftのログレベルは、TraceDebugInformationWarningErrorCriticalNoneとして定義され、数値が大きいほど重大なログを表します。

ログレベル用途
Trace最も詳細な追跡情報。通常は本番では出さない
Debug開発・検証時のデバッグ情報
Information通常処理の開始・終了・成功など
Warning異常ではないが注意が必要な状態
Error処理に失敗したがアプリ全体は継続可能なエラー
Criticalアプリ停止や重大障害につながる致命的エラー
Noneログを出力しない

基本的には、開発環境ではDebugInformationを活用し、本番環境ではInformationまたはWarning以上に制限することが多いです。

2. C#で使える標準Loggerの仕組み

2-1. Microsoft.Extensions.Loggingとは

Microsoft.Extensions.Loggingは、.NETで標準的に使われるログ抽象化ライブラリです。特定の出力先に依存せず、ILoggerという共通インターフェースを通じてログを記録できます。ASP.NET CoreのログフレームワークもMicrosoft.Extensions.Logging NuGetパッケージによって提供されます。

この仕組みにより、アプリケーション側のコードは次のように書けます。

C#
logger.LogInformation("User created. UserId={UserId}", userId);

実際にログをコンソールへ出すのか、ファイルへ出すのか、Azure Application Insightsへ送るのか、Serilogへ渡すのかは設定やProvider側で決められます。

2-2. ILogger・ILoggerFactory・ILoggerProviderの違い

C# Loggerを理解するには、ILoggerILoggerFactoryILoggerProviderの違いを押さえることが重要です。

名前役割
ILoggerアプリケーションコードからログを書くためのインターフェース
ILoggerFactoryILoggerを生成するファクトリ
ILoggerProvider実際の出力先に対応するログプロバイダー

通常のアプリケーション開発では、ILogger<T>をDIで受け取って使うことが多く、ILoggerFactoryILoggerProviderを直接扱う場面は多くありません。ILoggerが作成されるときにはカテゴリが指定され、そのカテゴリはログメッセージに含まれます。

2-3. .NET Core/.NET 5以降でのLoggerの考え方

.NET Core以降のC#開発では、Generic HostやDependency Injectionと組み合わせてLoggerを使うのが一般的です。アプリケーション全体でログ設定を一元管理し、各クラスではILogger<T>を受け取ってログを書きます。

たとえば、サービスクラスでは次のようにLoggerを注入します。

C#
public class OrderService
{
private readonly ILogger<OrderService> _logger;

public OrderService(ILogger<OrderService> logger)
{
_logger = logger;
}

public void CreateOrder(int orderId)
{
_logger.LogInformation("Order created. OrderId={OrderId}", orderId);
}
}

この書き方により、クラス名をカテゴリとしてログに含められます。障害調査時に「どのクラスから出たログか」が分かりやすくなります。

2-4. ASP.NET Coreで標準Loggerが使われる流れ

ASP.NET Coreでは、プロジェクト作成時点でログ機能が組み込まれています。Program.csWebApplication.CreateBuilder(args)を呼び出すと、設定ファイル、環境変数、ログプロバイダーなどが読み込まれます。

ControllerやMinimal APIでは、ILogger<T>をDIで受け取ってそのまま使えます。

C#
[ApiController]
[Route("api/[controller]")]
public class UsersController : ControllerBase
{
private readonly ILogger<UsersController> _logger;

public UsersController(ILogger<UsersController> logger)
{
_logger = logger;
}

[HttpGet("{id}")]
public IActionResult GetUser(int id)
{
_logger.LogInformation("Get user requested. UserId={UserId}", id);
return Ok();
}
}

ASP.NET Coreでは複数のログプロバイダーを有効化でき、コンソール、Debug、EventSource、EventLog、Application Insightsなどに出力できます。Microsoft公式ドキュメントでは、ログプロバイダーはログの出力先を構成するものであり、複数のProviderを同時に有効化できると説明されています。

2-5. appsettings.jsonによるログ設定の基本

ASP.NET Coreでは、appsettings.jsonでログレベルを設定できます。

JSON
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

この例では、アプリ全体の標準ログレベルをInformationにし、Microsoft.AspNetCoreカテゴリはWarning以上に制限しています。これにより、フレームワーク内部の詳細ログを抑えつつ、自分のアプリケーションのログは確認しやすくできます。

3. C# Loggerの基本的な実装方法

3-1. コンソールアプリでLoggerを使う方法

コンソールアプリでも、Microsoft.Extensions.Loggingを使ってLoggerを実装できます。まず、必要なパッケージを追加します。

Bash
dotnet add package Microsoft.Extensions.Hosting
dotnet add package Microsoft.Extensions.Logging.Console

次に、Program.csを次のように実装します。

C#
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;

var host = Host.CreateDefaultBuilder(args)
.ConfigureServices(services =>
{
services.AddTransient<App>();
})
.Build();

var app = host.Services.GetRequiredService<App>();
app.Run();

public class App
{
private readonly ILogger<App> _logger;

public App(ILogger<App> logger)
{
_logger = logger;
}

public void Run()
{
_logger.LogInformation("Application started.");
_logger.LogWarning("This is a warning message.");
_logger.LogError("This is an error message.");
}
}

この方法なら、コンソールアプリでもASP.NET Coreと同じ考え方でLoggerを使えます。

3-2. ASP.NET CoreでILoggerをDIして使う方法

ASP.NET Coreでは、ILogger<T>をコンストラクタで受け取るだけでLoggerを使えます。

C#
public class ProductService
{
private readonly ILogger<ProductService> _logger;

public ProductService(ILogger<ProductService> logger)
{
_logger = logger;
}

public void UpdateProduct(int productId)
{
_logger.LogInformation("Product update started. ProductId={ProductId}", productId);

// 更新処理

_logger.LogInformation("Product update completed. ProductId={ProductId}", productId);
}
}

ILogger<ProductService>ProductService部分はログカテゴリとして扱われます。カテゴリを使うことで、クラス単位や名前空間単位でログレベルを変更できます。

3-3. ログレベルごとの出力サンプル

ログレベルごとの出力例は次のとおりです。

C#
logger.LogTrace("Trace log. UserId={UserId}", userId);
logger.LogDebug("Debug log. UserId={UserId}", userId);
logger.LogInformation("Information log. UserId={UserId}", userId);
logger.LogWarning("Warning log. UserId={UserId}", userId);
logger.LogError("Error log. UserId={UserId}", userId);
logger.LogCritical("Critical log. UserId={UserId}", userId);

TraceDebugは詳細な調査向け、Informationは通常の処理記録、Warningは注意が必要な状態、ErrorCriticalは障害対応向けに使います。ログレベルは、重要度の分類だけでなく、出力対象をフィルタリングするためにも使われます。

3-4. 例外情報をログに出力する方法

例外をログに出す場合は、例外オブジェクトを第1引数に渡します。

C#
try
{
ExecutePayment(orderId);
}
catch (Exception ex)
{
logger.LogError(ex, "Payment failed. OrderId={OrderId}", orderId);
throw;
}

この形式にすると、例外メッセージだけでなくスタックトレースもログに含めやすくなります。悪い例として、次のように例外メッセージだけを文字列で出す方法は避けましょう。

C#
logger.LogError("Payment failed: " + ex.Message);

この書き方では、スタックトレースや例外型などの重要情報を失う可能性があります。

3-5. 構造化ログで変数やパラメータを記録する方法

C# Loggerでは、文字列補間よりもメッセージテンプレートを使うのがおすすめです。

悪い例です。

C#
logger.LogInformation($"User created. UserId={userId}");

良い例です。

C#
logger.LogInformation("User created. UserId={UserId}", userId);

後者はUserIdという名前付きプロパティとして扱いやすく、SerilogやApplication Insightsなどのログ分析基盤で検索・集計しやすくなります。Microsoftのログ機能でも、メッセージテンプレート内のプレースホルダー名がキーとなり、引数の値が対応する値として扱われます。

4. C# Loggerの設定方法

4-1. appsettings.jsonでログレベルを変更する

appsettings.jsonでログレベルを変更する基本形は次のとおりです。

JSON
{
"Logging": {
"LogLevel": {
"Default": "Information",
"MyApp": "Debug",
"Microsoft": "Warning",
"Microsoft.AspNetCore": "Warning"
}
}
}

この設定では、アプリ全体はInformation以上、MyAppカテゴリはDebug以上、Microsoft関連のログはWarning以上になります。ログレベルをカテゴリごとに設定できるため、必要なログだけを出力しやすくなります。

4-2. 環境別にログ設定を切り替える方法

ASP.NET Coreでは、appsettings.Development.jsonappsettings.Staging.jsonappsettings.Production.jsonのように環境別の設定ファイルを使えます。

開発環境の例です。

JSON
{
"Logging": {
"LogLevel": {
"Default": "Debug",
"Microsoft.AspNetCore": "Information"
}
}
}

本番環境の例です。

JSON
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

環境変数ASPNETCORE_ENVIRONMENTまたはDOTNET_ENVIRONMENTに応じて読み込まれる設定が変わります。開発環境では詳細ログを出し、本番環境では必要なログに絞る設計が基本です。

4-3. コンソール出力の設定

標準Loggerでコンソール出力を使う場合、AddConsole()を利用します。

C#
builder.Logging.ClearProviders();
builder.Logging.AddConsole();

ASP.NET Coreのテンプレートでは、既定でコンソールログが有効になっていることが多いです。ただし、独自にClearProviders()を呼び出した場合は、必要なProviderを再追加する必要があります。

C#
var builder = WebApplication.CreateBuilder(args);

builder.Logging.ClearProviders();
builder.Logging.AddConsole();
builder.Logging.AddDebug();

var app = builder.Build();
app.Run();

コンソールProviderは標準出力にログを表示するため、DockerやKubernetesなどのコンテナ環境でも扱いやすい出力先です。Microsoft公式ドキュメントでは、Console Providerはログを永続化せず標準出力へ表示するProviderとして説明されています。

4-4. ファイル出力の設定

Microsoft.Extensions.Loggingの標準機能だけでは、高機能なファイル出力やローテーションは十分ではありません。ファイルログを本格的に使う場合は、SerilogやNLogを使うのが一般的です。

Serilogでファイル出力する例です。

Bash
dotnet add package Serilog.AspNetCore
dotnet add package Serilog.Sinks.File
C#
using Serilog;

Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.WriteTo.File("logs/app-.log", rollingInterval: RollingInterval.Day)
.CreateLogger();

var builder = WebApplication.CreateBuilder(args);
builder.Host.UseSerilog();

var app = builder.Build();

app.MapGet("/", (ILogger<Program> logger) =>
{
logger.LogInformation("Hello log");
return "Hello";
});

app.Run();

NLogでファイル出力する場合は、nlog.configにFile Targetを設定します。NLogはコンソール、ファイル、データベースなど複数の出力先にログを送るための柔軟なログプラットフォームです。

4-5. ログフォーマットをカスタマイズする方法

ログフォーマットをカスタマイズすると、時刻、ログレベル、カテゴリ、メッセージ、例外、リクエストIDなどを見やすく出力できます。

Serilogの例です。

C#
Log.Logger = new LoggerConfiguration()
.WriteTo.Console(
outputTemplate: "[{Timestamp:yyyy-MM-dd HH:mm:ss} {Level:u3}] {Message:lj}{NewLine}{Exception}"
)
.CreateLogger();

NLogの例です。

XML
<target xsi:type="File"
name="file"
fileName="logs/app.log"
layout="${longdate} ${uppercase:${level}} ${logger} ${message} ${exception:format=tostring}" />

人が直接読むログではテキスト形式、ログ分析基盤に送るログではJSON形式が向いています。

4-6. ログローテーションと保存期間の考え方

本番環境でファイルログを使う場合、ログローテーションと保存期間を必ず設計します。ログローテーションとは、日単位、サイズ単位、起動単位などでログファイルを分割する仕組みです。

たとえば、Serilogでは日単位のローテーションを次のように設定できます。

C#
.WriteTo.File(
"logs/app-.log",
rollingInterval: RollingInterval.Day,
retainedFileCountLimit: 30
)

この例では、日ごとにログファイルを分け、保存数を30個に制限します。ログを無制限に保存するとディスク容量を圧迫するため、保存期間、圧縮、転送、削除ルールを運用前に決めておきましょう。

5. C#でおすすめのLoggerライブラリ比較

5-1. Serilogの特徴と向いている用途

Serilogは、構造化ログに強いC# Loggerライブラリです。メッセージ内のプロパティをJSONやログ分析基盤で扱いやすく記録できます。公式サイトでも、Serilogはファイルやコンソールなどに診断ログを出力でき、構造化イベントデータを重視して設計されていると説明されています。

Serilogが向いている用途は、次のようなケースです。

用途理由
Web APIリクエストIDやユーザーIDを構造化して残しやすい
マイクロサービスJSONログや外部Sinkとの連携がしやすい
クラウド環境標準出力、ファイル、ログ基盤へ柔軟に出力できる
分析重視のシステムプロパティ検索や集計に向いている

特に、Elastic Stack、Seq、Datadog、Application Insightsなどと連携する場合に便利です。

5-2. NLogの特徴と向いている用途

NLogは、設定の柔軟性と出力先の豊富さが特徴のLoggerライブラリです。公式サイトでは、NLogは.NET Standardを含むさまざまな.NETプラットフォームに対応し、コンソール、ファイル、データベースなど複数のTargetへログを書き込めると説明されています。

NLogが向いている用途は、次のようなケースです。

用途理由
ファイルログ中心の業務システムファイル出力やルーティング設定が豊富
既存システムXML設定で運用しやすい
エラー別ファイル分離ルール設定で出力先を分けやすい
Windowsアプリ従来型の.NETアプリでも使いやすい

NLogは、構造化ログと従来型ログの両方をサポートしています。

5-3. log4netの特徴と注意点

log4netは、長く使われてきた.NET向けログライブラリです。既存システムで採用されていることも多く、過去の資産を活かしたい場合には選択肢になります。

一方で、新規のASP.NET Core開発では、SerilogやNLogの方が導入事例や構造化ログの扱いやすさで選ばれることが多いです。ASP.NET Coreでlog4netを使う場合は、Microsoft.Extensions.Logging向けのProviderパッケージを使う方法があります。NuGetでは、ASP.NET Coreアプリケーションでlog4netをMicrosoft Extensions Loggingのハンドラーとして構成できるパッケージが公開されています。

5-4. Microsoft.Extensions.Loggingとの使い分け

Microsoft.Extensions.Loggingはログを書くための共通インターフェースとして使い、SerilogやNLogは実際の出力や保存を担当するライブラリとして使うのが分かりやすい考え方です。

アプリケーションコードでは次のように標準のILogger<T>を使います。

C#
logger.LogInformation("Order completed. OrderId={OrderId}", orderId);

その裏側の出力先をSerilogやNLogに差し替えます。この設計にしておくと、ログライブラリを変更してもアプリケーションコードへの影響を小さくできます。

5-5. Serilog・NLog・log4netの比較表

項目SerilogNLoglog4net
得意分野構造化ログ、JSONログ、クラウド連携柔軟な出力先設定、ファイルログ、ルーティング既存システム、従来型ログ
設定方法C#コード、appsettings.jsonXML、appsettings.jsonXML
構造化ログ強い対応限定的
ASP.NET Coreとの相性高い高い追加Providerで対応
初心者向け分かりやすい設定項目は多いが強力新規採用では優先度低め
おすすめ用途Web API、クラウド、分析基盤連携業務アプリ、ファイルログ中心既存log4net資産の継続利用

5-6. 初心者におすすめのLoggerライブラリ

初心者には、まず標準のMicrosoft.Extensions.LoggingILogger<T>を理解することをおすすめします。そのうえで、ファイル出力やJSONログが必要になったらSerilogを導入すると分かりやすいです。

おすすめの順番は次のとおりです。

段階使うLogger
Loggerの基本学習Microsoft.Extensions.Logging
コンソール・ASP.NET Coreの基本ログILogger<T>
ファイル出力・JSONログSerilog
複雑なファイル分岐・既存業務システムNLog
既存資産の保守log4net

新規開発では、ILogger<T>をアプリケーションコードの標準にして、出力先としてSerilogまたはNLogを選ぶ構成が扱いやすいです。

6. Serilogを使ったC#ログ出力の実装例

6-1. Serilogのインストール方法

ASP.NET CoreでSerilogを使う場合は、まずパッケージを追加します。

Bash
dotnet add package Serilog.AspNetCore
dotnet add package Serilog.Sinks.Console
dotnet add package Serilog.Sinks.File

JSON形式でファイル出力したい場合は、次のパッケージも追加します。

Bash
dotnet add package Serilog.Formatting.Compact

Serilogは、コンソール、ファイル、その他の出力先へ診断ログを送れる.NET向けログライブラリです。構造化ログを重視する設計のため、C# LoggerとしてWeb APIやクラウド環境でよく使われます。

6-2. コンソールにログ出力する方法

Serilogでコンソールにログを出す基本例です。

C#
using Serilog;

Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console()
.CreateLogger();

Log.Information("Application started.");
Log.Warning("Disk space is low. FreeSpaceMb={FreeSpaceMb}", 512);
Log.Error("Application error occurred.");

Log.CloseAndFlush();

ASP.NET Coreでは、builder.Host.UseSerilog()を使って標準Loggerと統合します。

C#
using Serilog;

Log.Logger = new LoggerConfiguration()
.WriteTo.Console()
.CreateLogger();

var builder = WebApplication.CreateBuilder(args);
builder.Host.UseSerilog();

var app = builder.Build();

app.MapGet("/", (ILogger<Program> logger) =>
{
logger.LogInformation("Request received.");
return "OK";
});

app.Run();

6-3. ファイルにログ出力する方法

Serilogでファイルにログ出力する例です。

C#
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.WriteTo.File(
path: "logs/app-.log",
rollingInterval: RollingInterval.Day,
retainedFileCountLimit: 30
)
.CreateLogger();

この設定では、logsフォルダに日単位でログファイルが作成されます。retainedFileCountLimitを設定しておくと、古いログファイルを一定数で整理できます。

6-4. JSON形式でログ出力する方法

ログ分析基盤に送る場合は、JSON形式が便利です。

C#
using Serilog;
using Serilog.Formatting.Compact;

Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.WriteTo.File(
new CompactJsonFormatter(),
"logs/app-.json",
rollingInterval: RollingInterval.Day
)
.CreateLogger();

Log.Information("Order completed. OrderId={OrderId}, Amount={Amount}", 1001, 2500);

JSONログにすると、OrderIdAmountのようなプロパティを検索・集計しやすくなります。構造化ログを重視するSerilogの強みが活きる使い方です。

6-5. ASP.NET CoreでSerilogを設定する方法

ASP.NET CoreでSerilogを使う基本形は次のとおりです。

C#
using Serilog;

Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.WriteTo.Console()
.WriteTo.File("logs/app-.log", rollingInterval: RollingInterval.Day)
.CreateLogger();

try
{
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseSerilog();

var app = builder.Build();

app.MapGet("/", (ILogger<Program> logger) =>
{
logger.LogInformation("Home endpoint called.");
return Results.Ok("Hello");
});

app.Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "Application terminated unexpectedly.");
}
finally
{
Log.CloseAndFlush();
}

アプリ起動時の例外も記録したい場合は、try-catch-finallyで囲み、最後にLog.CloseAndFlush()を呼び出します。

6-6. Serilogでよく使うSinkの種類

Serilogでは、ログの出力先をSinkと呼びます。よく使うSinkは次のとおりです。

Sink用途
Consoleコンソール出力
Fileファイル出力
SeqSerilogと相性のよいログサーバー
Elasticsearch検索・分析基盤への出力
Application InsightsAzure環境での監視
Datadogクラウド監視サービス連携
Slack / Email重大エラー通知

最初はConsoleとFileから始め、必要に応じて監視ツールやログ分析基盤に送る構成へ拡張するとよいでしょう。

7. NLogを使ったC#ログ出力の実装例

7-1. NLogのインストール方法

ASP.NET CoreでNLogを使う場合は、次のパッケージを追加します。

Bash
dotnet add package NLog.Web.AspNetCore

コンソールアプリやGeneric Hostで使う場合は、次のパッケージも選択肢になります。

Bash
dotnet add package NLog.Extensions.Logging

NLogは、コンソール、ファイル、データベースなどのTargetにログを出力でき、設定の柔軟性が高いLoggerライブラリです。公式サイトでは、高性能、使いやすさ、拡張性、柔軟な設定が特徴として挙げられています。

7-2. nlog.configの基本設定

NLogでは、nlog.configに出力先とルールを定義します。

XML
<?xml version="1.0" encoding="utf-8" ?>
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

<targets>
<target xsi:type="Console"
name="console"
layout="${longdate} ${uppercase:${level}} ${logger} ${message} ${exception:format=tostring}" />

<target xsi:type="File"
name="file"
fileName="logs/app.log"
layout="${longdate} ${uppercase:${level}} ${logger} ${message} ${exception:format=tostring}" />
</targets>

<rules>
<logger name="*" minlevel="Info" writeTo="console,file" />
</rules>
</nlog>

この設定では、Info以上のログをコンソールとファイルに出力します。

7-3. ファイルログを出力する方法

ASP.NET CoreでNLogを使う場合、Program.csは次のように設定します。

C#
using NLog.Web;

var logger = NLogBuilder.ConfigureNLog("nlog.config").GetCurrentClassLogger();

try
{
var builder = WebApplication.CreateBuilder(args);

builder.Logging.ClearProviders();
builder.Host.UseNLog();

var app = builder.Build();

app.MapGet("/", (ILogger<Program> log) =>
{
log.LogInformation("NLog sample endpoint called.");
return "OK";
});

app.Run();
}
catch (Exception ex)
{
logger.Error(ex, "Application stopped because of exception.");
throw;
}
finally
{
NLog.LogManager.Shutdown();
}

nlog.configでFile Targetを設定しておけば、ログファイルが作成されます。

7-4. エラーログだけを別ファイルに出力する方法

NLogでは、ルールを分けることでエラーログだけ別ファイルに出力できます。

XML
<targets>
<target xsi:type="File"
name="allFile"
fileName="logs/all.log"
layout="${longdate} ${level} ${logger} ${message} ${exception:format=tostring}" />

<target xsi:type="File"
name="errorFile"
fileName="logs/error.log"
layout="${longdate} ${level} ${logger} ${message} ${exception:format=tostring}" />
</targets>

<rules>
<logger name="*" minlevel="Info" writeTo="allFile" />
<logger name="*" minlevel="Error" writeTo="errorFile" />
</rules>

この設定では、通常ログはall.logへ、Error以上のログはerror.logにも出力されます。

7-5. ASP.NET CoreでNLogを使う方法

ASP.NET CoreでNLogを使う基本手順は、次の流れです。

  1. NLog.Web.AspNetCoreをインストールする

  2. nlog.configを作成する

  3. Program.csUseNLog()を呼び出す

  4. 各クラスではILogger<T>を使う

アプリケーションコードでは、Serilogの場合と同じように標準のILogger<T>を使えます。

C#
public class ReportService
{
private readonly ILogger<ReportService> _logger;

public ReportService(ILogger<ReportService> logger)
{
_logger = logger;
}

public void Generate()
{
_logger.LogInformation("Report generation started.");
}
}

出力先をNLogにしても、アプリケーションコードは標準Loggerのまま保てます。

7-6. NLogでログが出ないときの確認ポイント

NLogでログが出ない場合は、次の点を確認します。

確認項目内容
nlog.configの配置実行ディレクトリにコピーされているか
ファイル名nlog.configの名前が正しいか
minlevel出力したいログレベルがルールに含まれているか
writeToTarget名と一致しているか
フォルダ権限ログ出力先に書き込み権限があるか
ClearProvidersProviderを消した後にUseNLog()しているか
内部ログNLogのinternalLogFileで設定エラーを確認する

設定ファイルのミスは多いため、まずはConsole Targetだけで出るか確認し、その後File Targetを追加すると切り分けしやすくなります。

8. C# Loggerでよくあるエラーと解決策

8-1. ログが出力されない原因と対処法

ログが出力されない原因として最も多いのは、ログレベルの設定です。たとえば、DefaultWarningになっている場合、LogInformation()は出力されません。

JSON
{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
}
}

この設定でInformationログを出したい場合は、次のように変更します。

JSON
{
"Logging": {
"LogLevel": {
"Default": "Information"
}
}
}

また、builder.Logging.ClearProviders()を呼び出している場合は、AddConsole()UseSerilog()UseNLog()などを再設定しているか確認します。

8-2. appsettings.jsonの設定が反映されない原因

appsettings.jsonの設定が反映されない場合は、次を確認します。

原因対処法
環境別設定に上書きされているappsettings.Development.jsonなどを確認する
JSONの階層が間違っているLogging:LogLevel配下に設定する
カテゴリ名が違うクラス名・名前空間に合わせる
実行環境が想定と違うASPNETCORE_ENVIRONMENTを確認する
設定ファイルがコピーされていないcsprojのCopy設定を確認する

appsettings.jsonよりも環境別設定や環境変数が優先されるケースがあるため、実際に読み込まれている環境を確認することが重要です。

8-3. ILoggerがDIできない場合の対処法

ILogger<T>がDIできない場合、クラスの生成方法を確認します。ASP.NET CoreのDIコンテナから生成されるController、Service、HostedServiceなどでは通常そのまま注入できます。

C#
public class MyService
{
private readonly ILogger<MyService> _logger;

public MyService(ILogger<MyService> logger)
{
_logger = logger;
}
}

ただし、次のようにnewで直接インスタンス化している場合、DIは働きません。

C#
var service = new MyService(); // ILoggerを渡せない

この場合は、DIに登録して取得します。

C#
builder.Services.AddScoped<MyService>();

また、静的クラスでLoggerを使いたい場合は設計を見直し、可能であればインスタンスクラスにしてILogger<T>を注入する方が保守しやすくなります。

8-4. ファイルログが作成されない原因

ファイルログが作成されない場合は、次の点を確認します。

原因対処法
出力先フォルダが存在しないフォルダを作成する、または自動作成される設定にする
書き込み権限がない実行ユーザーの権限を確認する
相対パスの基準が違う実行ディレクトリを確認する
ログレベルが対象外InformationErrorが出力対象か確認する
パッケージ不足File SinkやNLog Webパッケージを確認する
設定ファイル未コピーnlog.configappsettings.jsonの出力設定を確認する

Windowsサービス、IIS、Docker、Linux環境では、実行ユーザーや作業ディレクトリが開発環境と異なることがあります。絶対パスや環境変数を使って出力先を明確にするとトラブルを減らせます。

8-5. ログレベルを変更しても出力内容が変わらない原因

ログレベルを変更しても出力内容が変わらない場合は、次の可能性があります。

原因確認内容
別の設定ファイルが優先されている環境別appsettingsを確認
カテゴリ単位の設定に上書きされているMicrosoftや自社名前空間の設定を確認
SerilogやNLog側で別設定がある独自のMinimumLevelやminlevelを確認
アプリを再起動していない設定変更後に再起動する
Providerごとの設定があるConsole、Debugなどの個別設定を確認

Microsoftのログ設定では、ログレベルはアプリ全体だけでなく特定カテゴリにも設定できます。カテゴリ単位の設定が優先されていると、想定したログが出ないことがあります。

8-6. 本番環境だけログが出ない場合の確認ポイント

本番環境だけログが出ない場合は、開発環境との差分を確認します。

確認項目内容
環境名Production用設定になっているか
ログレベルWarning以上になっていないか
権限ログ出力先に書けるか
パス相対パスが想定どおりか
コンテナ標準出力に出す設計か
IISアプリケーションプールIDの権限はあるか
クラウドログ収集設定が有効か

本番環境では、ファイルに直接保存するよりも、標準出力や監視サービスに送る設計の方が運用しやすい場合があります。

9. C# Loggerの実践的な活用パターン

9-1. Web APIのリクエスト・レスポンスをログに残す

Web APIでは、リクエストID、HTTPメソッド、パス、ステータスコード、処理時間をログに残すと便利です。

C#
app.Use(async (context, next) =>
{
var logger = context.RequestServices.GetRequiredService<ILogger<Program>>();
var start = DateTime.UtcNow;

try
{
await next();

var elapsedMs = (DateTime.UtcNow - start).TotalMilliseconds;

logger.LogInformation(
"HTTP {Method} {Path} responded {StatusCode} in {ElapsedMs} ms",
context.Request.Method,
context.Request.Path,
context.Response.StatusCode,
elapsedMs
);
}
catch (Exception ex)
{
logger.LogError(
ex,
"HTTP {Method} {Path} failed",
context.Request.Method,
context.Request.Path
);
throw;
}
});

ただし、リクエストボディやレスポンスボディをすべてログに残すと、個人情報の漏えいやログ容量の増大につながります。必要な項目だけを選んで記録しましょう。

9-2. バッチ処理の開始・終了・失敗を記録する

バッチ処理では、開始、終了、処理件数、失敗理由を必ず記録します。

C#
public async Task RunAsync()
{
var batchId = Guid.NewGuid();

_logger.LogInformation("Batch started. BatchId={BatchId}", batchId);

try
{
var count = await ExecuteAsync();

_logger.LogInformation(
"Batch completed. BatchId={BatchId}, Count={Count}",
batchId,
count
);
}
catch (Exception ex)
{
_logger.LogError(ex, "Batch failed. BatchId={BatchId}", batchId);
throw;
}
}

バッチIDを付けておくと、複数のログをまとめて追跡できます。

9-3. データベース処理のエラーをログに残す

データベース処理では、対象ID、処理名、例外情報を残すと調査しやすくなります。

C#
try
{
await _dbContext.SaveChangesAsync();
}
catch (DbUpdateException ex)
{
_logger.LogError(
ex,
"Database update failed. Entity={Entity}, EntityId={EntityId}",
"Order",
orderId
);

throw;
}

SQL文やパラメータをすべて出すと機密情報を含む可能性があるため、ログに出す項目は慎重に選びます。

9-4. ユーザー操作ログとシステムログの分け方

ユーザー操作ログとシステムログは目的が異なります。

種類目的
ユーザー操作ログ監査、問い合わせ対応ログイン、更新、削除、承認
システムログ障害調査、監視例外、API失敗、DBエラー
アクセスログ通信履歴URL、ステータスコード、処理時間
セキュリティログ不正検知認証失敗、権限エラー

同じLoggerで出しても構いませんが、カテゴリ、イベントID、出力先、ログフォーマットを分けると運用しやすくなります。

C#
_logger.LogInformation(
"User operation. Operation={Operation}, UserId={UserId}, TargetId={TargetId}",
"DeleteOrder",
userId,
orderId
);

9-5. 分散トレーシングや監視ツールと連携する方法

複数サービスで構成されるシステムでは、ログだけでなく分散トレーシングやメトリクスと組み合わせることが重要です。リクエストID、トレースID、スパンIDをログに含めると、サービスをまたいだ処理を追跡できます。

ASP.NET Coreでは、HttpContext.TraceIdentifierActivity.Currentを使って相関IDを扱えます。

C#
var traceId = System.Diagnostics.Activity.Current?.TraceId.ToString();

_logger.LogInformation("Processing request. TraceId={TraceId}", traceId);

SerilogやNLogを使えば、Application Insights、OpenTelemetry、Datadog、Elastic Stackなどの監視基盤と連携しやすくなります。MicrosoftのログProviderにはApplication Insights向けProviderもあり、Azure環境でログやテレメトリを扱う構成に利用できます。

10. C# Loggerのベストプラクティス

10-1. 適切なログレベルを使い分ける

ログレベルは、重要度に応じて一貫して使い分けます。

レベル判断基準
Trace非常に詳細な内部処理
Debug開発者向けの調査情報
Information正常な業務イベント
Warning処理は継続できるが注意が必要
Error具体的な処理失敗
Criticalシステム停止級の重大障害

何でもErrorにすると本当に重要な障害が埋もれます。逆に、失敗しているのにInformationで出すと監視に引っかかりません。

10-2. ログを出しすぎないための設計

ログを出しすぎると、次の問題が発生します。

問題影響
ディスク容量の圧迫サーバー停止やログ欠落
検索性の低下重要ログを見つけにくい
コスト増加クラウドログ基盤の料金増
パフォーマンス低下I/O負荷や送信負荷が増える
機密情報混入リスク情報漏えいにつながる

ログは「後から原因を追える最小限の情報」を意識して設計します。ループ内で大量に出すログや、リクエスト本文の丸ごと出力は慎重に扱いましょう。

10-3. 個人情報や機密情報をログに残さない

ログには、次の情報を原則として残さないようにします。

記録を避ける情報
認証情報パスワード、アクセストークン、APIキー
金融情報クレジットカード番号、口座番号
個人番号マイナンバー、社会保障番号
秘密情報秘密鍵、接続文字列
セッション情報Cookie、リフレッシュトークン

必要な場合は、次のようにマスキングします。

C#
logger.LogInformation("Payment requested. CardLast4={CardLast4}", cardNumber[^4..]);

ログは開発者、運用者、外部監視サービスなど複数の場所で閲覧される可能性があるため、アプリケーションデータ以上に慎重な管理が必要です。

10-4. 例外ログに必要な情報を含める

例外ログには、例外オブジェクト、処理名、対象ID、相関IDを含めると調査しやすくなります。

C#
catch (Exception ex)
{
_logger.LogError(
ex,
"Order cancellation failed. OrderId={OrderId}, UserId={UserId}, TraceId={TraceId}",
orderId,
userId,
traceId
);

throw;
}

例外を握りつぶしてログだけ出すと、呼び出し元が失敗を検知できないことがあります。必要に応じて再スローし、エラーハンドリングの責務を明確にします。

10-5. 検索しやすいログメッセージを書く

検索しやすいログメッセージには、次の特徴があります。

ポイント
イベント名が明確Order createdPayment failed
対象IDがあるOrderId={OrderId}
表記が統一されているUserIduser_idを混在させない
構造化されている文字列連結ではなくテンプレートを使う
原因が分かる例外情報やステータスコードを含める

悪い例です。

C#
_logger.LogError("エラーが発生しました");

良い例です。

C#
_logger.LogError(
ex,
"Payment failed. OrderId={OrderId}, PaymentProvider={PaymentProvider}",
orderId,
paymentProvider
);

10-6. 本番運用を意識したログ設計を行う

本番運用では、ログを「出す」だけでなく、「集める」「検索する」「通知する」「保存する」「削除する」まで考える必要があります。

観点設計内容
出力先標準出力、ファイル、監視基盤
ログレベル本番ではInformationまたはWarning以上
保存期間監査要件や容量に応じて決定
通知Criticalや特定Errorをアラート化
検索TraceId、UserId、OrderIdで検索可能にする
セキュリティ個人情報・機密情報を除外する
コストログ量と保存期間を管理する

C# Loggerは、開発時のデバッグ用ではなく、運用を支える重要な仕組みとして設計することが大切です。

11. C# Loggerに関するよくある質問

11-1. C#で標準Loggerだけを使っても問題ない?

小規模なアプリケーションやコンソール出力だけで十分な場合は、標準のMicrosoft.Extensions.Loggingだけでも問題ありません。ASP.NET Coreでは標準Loggerが最初から統合されており、ILogger<T>を使ってログを記録できます。

ただし、ファイル出力、ログローテーション、JSONログ、外部ログ基盤との連携が必要な場合は、SerilogやNLogを組み合わせる方が実用的です。

11-2. SerilogとNLogはどちらがおすすめ?

新規のWeb APIやクラウド環境では、構造化ログに強いSerilogがおすすめです。ファイルログ中心の業務システムや、細かいルールで出力先を分けたい場合はNLogが向いています。

条件おすすめ
JSONログを重視Serilog
クラウド・マイクロサービスSerilog
ファイル出力を細かく制御NLog
XML設定で運用したいNLog
既存システムがNLogNLog継続
初心者が学びやすい構造化ログSerilog

どちらを選んでも、アプリケーションコードではILogger<T>を使う設計にしておくと、将来的な変更に強くなります。

11-3. Console.WriteLineをLogger代わりに使ってもよい?

簡単なサンプルや一時的な確認であればConsole.WriteLineでも問題ありません。しかし、実際のアプリケーションではLoggerを使うべきです。

Loggerには、ログレベル、カテゴリ、例外情報、出力先切り替え、環境別設定、構造化ログといった機能があります。Console.WriteLineだけでは、本番運用や障害調査に必要な情報を安定して残すのが難しくなります。

11-4. ログファイルの保存場所はどこがよい?

ログファイルの保存場所は、実行環境によって変わります。

環境保存場所の考え方
開発環境プロジェクト配下のlogsフォルダ
Windowsサービスアプリ専用フォルダ、またはProgramData配下
IIS書き込み権限のある専用フォルダ
Linux/var/log/アプリ名など
Dockerファイルより標準出力を優先
クラウド監視基盤やログ収集サービスを優先

本番環境では、書き込み権限、容量、バックアップ、ローテーション、セキュリティを考慮して保存場所を決めます。

11-5. 本番環境ではどのログレベルにすべき?

一般的には、本番環境ではInformation以上またはWarning以上にすることが多いです。

システム状態推奨ログレベル
通常運用Information
ログ量を抑えたいWarning
障害調査中Debugを一時的に有効化
高負荷環境Warning以上を基本に必要箇所だけInformation

ただし、業務イベントを追跡したい場合はInformationが必要です。ログ量と調査性のバランスを見ながら決めましょう。

11-6. Loggerのパフォーマンス影響はどの程度?

Loggerのパフォーマンス影響は、ログ量、出力先、同期・非同期、フォーマット、外部送信の有無によって変わります。特に、ファイルI/Oやネットワーク送信を大量に行う場合は注意が必要です。

パフォーマンスを意識する場合は、次の点を守ります。

対策内容
適切なログレベルにする不要なDebugやTraceを本番で出さない
構造化ログを使う文字列連結や過剰な整形を避ける
大量ループで出しすぎない件数を集約して出す
非同期Sinkを検討する外部送信の待ちを減らす
ログ基盤側でサンプリングする高トラフィック環境で有効
必要な項目だけ出す大きなオブジェクトを丸ごと出さない

ログは障害対応に不可欠ですが、出しすぎると負荷やコストの原因になります。必要十分なログを設計することが重要です。

まとめ

C# Loggerは、アプリケーションの品質、保守性、運用性を高めるために欠かせない仕組みです。Console.WriteLineでも簡単な出力はできますが、実際の開発ではMicrosoft.Extensions.LoggingILogger<T>を使い、ログレベル、カテゴリ、例外情報、構造化ログを適切に扱うことが重要です。

C#でLoggerを使う基本方針は、まず標準のILogger<T>をアプリケーションコードに採用することです。そのうえで、コンソール出力だけで十分なら標準Loggerを使い、ファイル出力やJSONログ、ログローテーション、外部監視ツールとの連携が必要になったらSerilogやNLogを導入します。

Serilogは構造化ログやクラウド連携に強く、NLogはファイル出力や柔軟なルーティングに強いLoggerライブラリです。log4netは既存システムの保守では選択肢になりますが、新規開発ではSerilogまたはNLogを検討するとよいでしょう。

本番運用では、ログレベル、保存期間、個人情報対策、ログローテーション、監視連携、検索しやすいメッセージ設計まで考える必要があります。適切に設計されたC# Loggerは、障害発生時の原因調査を早め、システムの信頼性を高める強力な武器になります。