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のログレベルは、Trace、Debug、Information、Warning、Error、Critical、Noneとして定義され、数値が大きいほど重大なログを表します。
| ログレベル | 用途 |
|---|---|
| Trace | 最も詳細な追跡情報。通常は本番では出さない |
| Debug | 開発・検証時のデバッグ情報 |
| Information | 通常処理の開始・終了・成功など |
| Warning | 異常ではないが注意が必要な状態 |
| Error | 処理に失敗したがアプリ全体は継続可能なエラー |
| Critical | アプリ停止や重大障害につながる致命的エラー |
| None | ログを出力しない |
基本的には、開発環境ではDebugやInformationを活用し、本番環境では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を理解するには、ILogger、ILoggerFactory、ILoggerProviderの違いを押さえることが重要です。
| 名前 | 役割 |
|---|---|
| ILogger | アプリケーションコードからログを書くためのインターフェース |
| ILoggerFactory | ILoggerを生成するファクトリ |
| ILoggerProvider | 実際の出力先に対応するログプロバイダー |
通常のアプリケーション開発では、ILogger<T>をDIで受け取って使うことが多く、ILoggerFactoryやILoggerProviderを直接扱う場面は多くありません。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.csでWebApplication.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を実装できます。まず、必要なパッケージを追加します。
Bashdotnet 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);
TraceやDebugは詳細な調査向け、Informationは通常の処理記録、Warningは注意が必要な状態、ErrorやCriticalは障害対応向けに使います。ログレベルは、重要度の分類だけでなく、出力対象をフィルタリングするためにも使われます。
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.json、appsettings.Staging.json、appsettings.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でファイル出力する例です。
Bashdotnet 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の比較表
| 項目 | Serilog | NLog | log4net |
|---|---|---|---|
| 得意分野 | 構造化ログ、JSONログ、クラウド連携 | 柔軟な出力先設定、ファイルログ、ルーティング | 既存システム、従来型ログ |
| 設定方法 | C#コード、appsettings.json | XML、appsettings.json | XML |
| 構造化ログ | 強い | 対応 | 限定的 |
| ASP.NET Coreとの相性 | 高い | 高い | 追加Providerで対応 |
| 初心者向け | 分かりやすい | 設定項目は多いが強力 | 新規採用では優先度低め |
| おすすめ用途 | Web API、クラウド、分析基盤連携 | 業務アプリ、ファイルログ中心 | 既存log4net資産の継続利用 |
5-6. 初心者におすすめのLoggerライブラリ
初心者には、まず標準のMicrosoft.Extensions.LoggingとILogger<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を使う場合は、まずパッケージを追加します。
Bashdotnet add package Serilog.AspNetCore
dotnet add package Serilog.Sinks.Console
dotnet add package Serilog.Sinks.File
JSON形式でファイル出力したい場合は、次のパッケージも追加します。
Bashdotnet 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ログにすると、OrderIdやAmountのようなプロパティを検索・集計しやすくなります。構造化ログを重視する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 | ファイル出力 |
| Seq | Serilogと相性のよいログサーバー |
| Elasticsearch | 検索・分析基盤への出力 |
| Application Insights | Azure環境での監視 |
| Datadog | クラウド監視サービス連携 |
| Slack / Email | 重大エラー通知 |
最初はConsoleとFileから始め、必要に応じて監視ツールやログ分析基盤に送る構成へ拡張するとよいでしょう。
7. NLogを使ったC#ログ出力の実装例
7-1. NLogのインストール方法
ASP.NET CoreでNLogを使う場合は、次のパッケージを追加します。
Bashdotnet add package NLog.Web.AspNetCore
コンソールアプリやGeneric Hostで使う場合は、次のパッケージも選択肢になります。
Bashdotnet 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を使う基本手順は、次の流れです。
NLog.Web.AspNetCoreをインストールするnlog.configを作成するProgram.csでUseNLog()を呼び出す各クラスでは
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 | 出力したいログレベルがルールに含まれているか |
| writeTo | Target名と一致しているか |
| フォルダ権限 | ログ出力先に書き込み権限があるか |
| ClearProviders | Providerを消した後にUseNLog()しているか |
| 内部ログ | NLogのinternalLogFileで設定エラーを確認する |
設定ファイルのミスは多いため、まずはConsole Targetだけで出るか確認し、その後File Targetを追加すると切り分けしやすくなります。
8. C# Loggerでよくあるエラーと解決策
8-1. ログが出力されない原因と対処法
ログが出力されない原因として最も多いのは、ログレベルの設定です。たとえば、DefaultがWarningになっている場合、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. ファイルログが作成されない原因
ファイルログが作成されない場合は、次の点を確認します。
| 原因 | 対処法 |
|---|---|
| 出力先フォルダが存在しない | フォルダを作成する、または自動作成される設定にする |
| 書き込み権限がない | 実行ユーザーの権限を確認する |
| 相対パスの基準が違う | 実行ディレクトリを確認する |
| ログレベルが対象外 | InformationやErrorが出力対象か確認する |
| パッケージ不足 | File SinkやNLog Webパッケージを確認する |
| 設定ファイル未コピー | nlog.configやappsettings.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.TraceIdentifierやActivity.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 created、Payment failed |
| 対象IDがある | OrderId={OrderId} |
| 表記が統一されている | UserIdとuser_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 |
| 既存システムがNLog | NLog継続 |
| 初心者が学びやすい構造化ログ | 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.LoggingのILogger<T>を使い、ログレベル、カテゴリ、例外情報、構造化ログを適切に扱うことが重要です。
C#でLoggerを使う基本方針は、まず標準のILogger<T>をアプリケーションコードに採用することです。そのうえで、コンソール出力だけで十分なら標準Loggerを使い、ファイル出力やJSONログ、ログローテーション、外部監視ツールとの連携が必要になったらSerilogやNLogを導入します。
Serilogは構造化ログやクラウド連携に強く、NLogはファイル出力や柔軟なルーティングに強いLoggerライブラリです。log4netは既存システムの保守では選択肢になりますが、新規開発ではSerilogまたはNLogを検討するとよいでしょう。
本番運用では、ログレベル、保存期間、個人情報対策、ログローテーション、監視連携、検索しやすいメッセージ設計まで考える必要があります。適切に設計されたC# Loggerは、障害発生時の原因調査を早め、システムの信頼性を高める強力な武器になります。

