C#ログ出力の基本|ILogger・Serilogでファイル保存までわかる実践入門
はじめに
C#でアプリケーションを開発していると、「処理がどこまで進んだのか」「なぜエラーが発生したのか」「本番環境で何が起きているのか」を確認したい場面が必ず出てきます。そこで重要になるのがログ出力です。
ログは、単にコンソールへ文字を表示するだけではありません。開発中のデバッグ、本番環境での障害調査、処理履歴の確認、監視ツールとの連携など、アプリケーションを安定して運用するために欠かせない仕組みです。
C#では、簡単な確認であればConsole.WriteLineを使えますが、実務ではILoggerやSerilogなどのログライブラリを使うケースが一般的です。特にASP.NET CoreではILoggerが標準的に利用でき、さらにSerilogを組み合わせることで、ログをファイルに保存したり、JSON形式で出力したり、日付ごとにローテーションしたりできます。
この記事では、C#のログ出力の基本から、ILoggerの使い方、Serilogによるファイル保存、ログレベルの使い分け、よくあるエラーと対処法まで、実践で使える形で解説します。
1. C#のログ出力とは?まず押さえる基本
1-1. ログ出力の目的とデバッグとの違い
ログ出力とは、アプリケーションの動作状況やエラー情報、処理結果などを記録することです。ログを残しておくことで、あとから「いつ」「どの処理で」「何が起きたのか」を確認できます。
デバッグは、主に開発中にブレークポイントや変数確認を使って問題を調査する作業です。一方、ログ出力は開発中だけでなく、本番環境でも継続的に使われます。
たとえば、ユーザーから「画面でエラーが出た」と連絡があった場合、開発者が本番環境にデバッガーを接続して確認することは通常できません。その代わりに、アプリケーションが出力したログを確認して原因を調査します。
つまり、デバッグは開発中の調査手段、ログは運用中も含めた記録手段と考えるとわかりやすいです。
1-2. Console.WriteLineとログライブラリの違い
C#で文字を出力するだけなら、次のようにConsole.WriteLineを使えます。
C#Console.WriteLine("処理を開始しました");
これは簡単で便利ですが、実務のログ出力としては機能が足りないことが多いです。
ログライブラリを使うと、次のようなことができます。
C#_logger.LogInformation("処理を開始しました");
_logger.LogError(ex, "処理中にエラーが発生しました");
ログライブラリでは、ログレベル、日時、カテゴリ、例外情報、出力先の切り替えなどを管理できます。コンソールだけでなく、ファイル、データベース、クラウドログサービスなどへ出力することもできます。
Console.WriteLineは簡易確認向け、ILoggerやSerilogなどのログライブラリは実務向けと考えるとよいでしょう。
1-3. C#でログに残すべき情報
C#アプリケーションでログに残すべき情報は、あとから問題を調査するときに役立つ情報です。
代表的には、次のような情報があります。
・処理の開始と終了
・重要な処理の結果
・入力値や識別子
・外部APIやデータベースへのアクセス結果
・例外メッセージ
・スタックトレース
・ユーザーIDやリクエストIDなどの追跡情報
ただし、パスワード、クレジットカード番号、個人情報、認証トークンなどの機密情報をログに出力してはいけません。ログは調査に役立つ一方で、情報漏えいの原因にもなり得るためです。
ログには「原因調査に必要な情報」を残し、「漏れてはいけない情報」は出さないことが重要です。
1-4. ログ出力が必要になる代表的な場面
C#でログ出力が必要になる場面は多くあります。
たとえば、ASP.NET CoreのWebアプリでは、リクエストを受け付けたタイミング、認証に失敗したタイミング、API呼び出しでエラーが返ったタイミングなどでログが役立ちます。
バッチ処理では、処理対象件数、成功件数、失敗件数、処理時間などをログに残しておくと、実行結果をあとから確認できます。
Windowsサービスでは、画面がない状態で処理が動くため、ログがほぼ唯一の調査手段になることもあります。
また、障害が発生したときだけでなく、パフォーマンス改善や利用状況の分析にもログは活用できます。
2. C#で使える主なログ出力方法
2-1. Console.WriteLineで簡易的にログを出す方法
もっとも簡単なログ出力方法はConsole.WriteLineです。
C#using System;
class Program
{
static void Main()
{
Console.WriteLine("アプリケーションを開始しました");
int count = 10;
Console.WriteLine($"処理件数: {count}");
Console.WriteLine("アプリケーションを終了しました");
}
}
コンソールアプリや学習用コードでは、これだけでも処理の流れを確認できます。
ただし、Console.WriteLineにはログレベルの管理や出力先の切り替え機能がありません。また、本番環境でファイル保存したい場合や、ログの形式を統一したい場合には向いていません。
そのため、簡易確認にはConsole.WriteLine、本格的なログ出力にはILoggerやSerilogを使うのがおすすめです。
2-2. Debug.WriteLine・Trace.WriteLineの使い方
C#では、Debug.WriteLineやTrace.WriteLineを使ってログのような出力を行うこともできます。
C#using System.Diagnostics;
Debug.WriteLine("デバッグ用メッセージです");
Trace.WriteLine("トレース用メッセージです");
Debug.WriteLineは主にデバッグビルドでの確認に使われます。Visual Studioの出力ウィンドウにメッセージを表示できるため、開発中の確認に便利です。
Trace.WriteLineは、アプリケーションの実行状態を追跡する用途で使われます。
ただし、これらも本格的なログ管理にはやや不足があります。ログレベル、構造化ログ、出力先の柔軟な設定などを考えると、実務ではILoggerを使う方が扱いやすいです。
2-3. ILoggerを使った標準的なログ出力
ASP.NET Coreや.NETのアプリケーションでは、Microsoft.Extensions.LoggingのILoggerを使うのが標準的です。
C#public class SampleService
{
private readonly ILogger<SampleService> _logger;
public SampleService(ILogger<SampleService> logger)
{
_logger = logger;
}
public void Execute()
{
_logger.LogInformation("処理を開始しました");
_logger.LogWarning("注意が必要な状態です");
}
}
ILoggerを使うと、ログレベルごとに出力を制御できます。また、DIコンテナと組み合わせて利用できるため、ASP.NET Coreでは自然に導入できます。
さらに、SerilogやNLogなどの外部ライブラリと連携すれば、ILoggerの書き方を保ったまま、ファイル出力やJSON出力などに対応できます。
2-4. Serilog・NLog・log4netなど外部ライブラリの比較
C#でよく使われるログライブラリには、Serilog、NLog、log4netがあります。
Serilogは、構造化ログに強いライブラリです。メッセージの中に変数を埋め込むだけでなく、値をプロパティとして扱えるため、検索や分析に向いています。ASP.NET Coreとの連携もしやすく、ファイル出力やJSON出力にも対応できます。
NLogは、柔軟な設定と豊富な出力先が特徴です。ファイル、データベース、メール、外部サービスなど、さまざまなターゲットへログを出力できます。
log4netは歴史のあるログライブラリで、既存システムで使われていることがあります。ただし、新規開発ではSerilogやNLogが選ばれることが多いです。
新しくC#でログ出力を実装するなら、まずは標準のILoggerを使い、ファイル保存や高度な出力が必要になったらSerilogを組み合わせる構成が実践的です。
3. ILoggerでC#のログ出力を実装する基本
3-1. ILoggerとは何か
ILoggerは、.NETで標準的に使われるログ出力のインターフェースです。Microsoft.Extensions.Logging名前空間に含まれており、ASP.NET Coreアプリケーションでは標準で利用できます。
ILoggerそのものは、ログの書き方を定義する仕組みです。実際にどこへ出力するかは、コンソールプロバイダー、Debugプロバイダー、Serilogなどのログプロバイダーが担当します。
たとえば、次のようにログを出力できます。
C#_logger.LogInformation("ユーザー情報を取得しました");
_logger.LogError("ユーザー情報の取得に失敗しました");
アプリケーションのコード側ではILoggerを使い、出力先や形式は設定で変える。この分離がILoggerの大きな特徴です。
3-2. ILoggerを使うメリット
ILoggerを使うメリットは、ログ出力を標準的な形で統一できることです。
まず、ログレベルを使って出力内容を制御できます。開発環境では詳細なDebugログを出し、本番環境ではInformation以上だけ出すといった切り替えが可能です。
次に、DIと相性がよく、ASP.NET CoreではコンストラクタでILogger<T>を受け取るだけで利用できます。
また、出力先を変更しやすい点もメリットです。最初はコンソールに出力し、あとからSerilogを追加してファイル保存する場合でも、アプリケーション側のログ出力コードを大きく変える必要はありません。
3-3. ASP.NET CoreでILoggerを使う手順
ASP.NET Coreでは、ILoggerは標準でDIコンテナに登録されています。そのため、コントローラーやサービスのコンストラクタで受け取るだけで使えます。
C#using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("[controller]")]
public class SampleController : ControllerBase
{
private readonly ILogger<SampleController> _logger;
public SampleController(ILogger<SampleController> logger)
{
_logger = logger;
}
[HttpGet]
public IActionResult Get()
{
_logger.LogInformation("GETリクエストを受け付けました");
return Ok("success");
}
}
この例では、SampleControllerに対応したカテゴリ名でログが出力されます。カテゴリ名は通常、ILogger<T>のTに指定したクラス名になります。
ASP.NET Coreでは、リクエスト処理、サービス処理、例外処理など、さまざまな場所でILoggerを使えます。
3-4. コンソールアプリでILoggerを使う手順
コンソールアプリでもILoggerを使えます。一般的には、Microsoft.Extensions.Hostingを使ってGeneric Hostを構成します。
C#using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using IHost 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("コンソールアプリを開始しました");
}
}
この構成にすると、コンソールアプリでもASP.NET Coreに近い形でDIやログを利用できます。バッチ処理やWindowsサービスの土台としても使いやすい方法です。
3-5. ログレベルの種類と使い分け
ILoggerには、次のようなログレベルがあります。
Trace
Debug
Information
Warning
Error
Critical
None
Traceはもっとも詳細なログで、細かい処理の追跡に使います。Debugは開発中の確認に使います。Informationは通常の処理開始、終了、成功などを記録するレベルです。
Warningは処理は継続できるが注意が必要な状態に使います。Errorは処理の一部が失敗した場合に使います。Criticalはアプリケーション全体に影響する重大な障害に使います。
本番環境では、一般的にInformation以上を出力し、必要に応じてWarning以上に絞ることが多いです。
3-6. appsettings.jsonでログ設定を変更する方法
ASP.NET Coreでは、appsettings.jsonでログレベルを設定できます。
JSON{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
この例では、アプリケーション全体の既定ログレベルをInformationにし、Microsoft.AspNetCoreカテゴリのログはWarning以上だけ出力する設定です。
特定のクラスや名前空間だけログレベルを変えることもできます。
JSON{
"Logging": {
"LogLevel": {
"Default": "Warning",
"MyApp.Services": "Debug"
}
}
}
このように設定すると、通常はWarning以上に抑えつつ、MyApp.Services配下だけDebugログを出すことができます。
4. C#でログをファイルに出力する方法
4-1. 標準ILoggerだけでファイル出力できるのか
ILoggerはログ出力のインターフェースであり、標準の構成だけでは本格的なファイル出力機能は用意されていません。
.NET標準のログプロバイダーには、コンソール、Debug、EventSource、EventLogなどがありますが、実務でよく使う「日付ごとのログファイルを作成する」「一定サイズでローテーションする」「JSON形式で保存する」といったファイル出力は、外部ライブラリを使う方が簡単です。
そのため、C#でログをファイルに保存したい場合は、Serilog、NLog、log4netなどを利用するのが一般的です。
4-2. ファイル保存にはSerilogを使うのが実践的な理由
ファイル保存を行う場合、Serilogは非常に実践的な選択肢です。
理由は、ASP.NET CoreやILoggerとの連携がしやすく、設定もシンプルだからです。また、Serilog.Sinks.Fileを使えば、ログをファイルへ簡単に保存できます。
さらに、Serilogは構造化ログに強いため、次のような書き方ができます。
C#_logger.LogInformation("ユーザー {UserId} がログインしました", userId);
この場合、UserIdを単なる文字列ではなく、ログのプロパティとして扱えます。将来的にログ分析ツールへ送る場合にも便利です。
4-3. ログファイル出力の基本構成
C#でログをファイル出力する基本構成は、次のようになります。
アプリケーション
↓
ILogger
↓
Serilog
↓
File Sink
↓
logs/log-.txt
アプリケーション側のコードではILoggerを使い、実際の出力先としてSerilogを設定します。SerilogにはさまざまなSinkがありますが、ファイル保存にはSerilog.Sinks.Fileを使います。
この構成にしておくと、ログ出力コードをILoggerで統一しながら、出力先だけを柔軟に変更できます。
4-4. ログファイルの保存先を決めるポイント
ログファイルの保存先は、アプリケーションの種類や実行環境によって決めます。
開発中であれば、プロジェクト配下のlogsフォルダに保存すると確認しやすいです。
logs/app-.log
本番環境では、アプリケーションの実行ユーザーが書き込み可能な場所を指定する必要があります。Windowsサービスの場合、実行ユーザーによってはアプリケーションフォルダに書き込めないことがあります。
また、Dockerやクラウド環境では、ファイルに保存するよりも標準出力へ出してログ収集基盤に任せる方が適している場合もあります。
ログの保存先を決めるときは、次の点を確認しましょう。
・実行ユーザーに書き込み権限があるか
・ログを運用担当者が確認しやすいか
・ログローテーションや削除がしやすいか
・バックアップ対象に含める必要があるか
・機密情報を含む可能性があるか
4-5. ログローテーションの考え方
ログをファイルに保存する場合、ログローテーションが重要です。ログローテーションとは、ログファイルを日付やサイズごとに分割し、古いログを一定期間で削除する仕組みです。
ログローテーションを行わないと、1つのログファイルが肥大化し続け、ディスク容量を圧迫する可能性があります。
Serilogでは、次のように日付ごとのファイル出力ができます。
C#.WriteTo.File("logs/app-.log", rollingInterval: RollingInterval.Day)
この設定では、日付ごとにログファイルが分かれます。
さらに、保存するファイル数を制限できます。
C#.WriteTo.File(
"logs/app-.log",
rollingInterval: RollingInterval.Day,
retainedFileCountLimit: 30)
この例では、ログファイルを最大30個まで保持します。
5. SerilogでC#のログをファイル保存する実装手順
5-1. Serilogとは何か
Serilogは、.NET向けのログライブラリです。特に構造化ログを得意としており、ログメッセージに含めた値を検索や分析しやすい形で記録できます。
通常の文字列ログでは、次のように値が文章の中に埋め込まれます。
C#_logger.LogInformation($"ユーザー {userId} がログインしました");
一方、構造化ログでは次のように書きます。
C#_logger.LogInformation("ユーザー {UserId} がログインしました", userId);
この書き方をすると、UserIdをログの項目として扱えるため、ログ分析ツールとの相性がよくなります。
Serilogは、コンソール、ファイル、データベース、クラウドサービスなど、さまざまな出力先に対応できます。
5-2. 必要なNuGetパッケージを追加する
ASP.NET CoreでSerilogを使ってファイル出力する場合、代表的には次のパッケージを追加します。
Bashdotnet add package Serilog.AspNetCore
dotnet add package Serilog.Sinks.File
dotnet add package Serilog.Settings.Configuration
コンソールにもSerilog形式で出力したい場合は、次も追加します。
Bashdotnet add package Serilog.Sinks.Console
Serilog.AspNetCoreはASP.NET CoreとSerilogを連携するためのパッケージです。Serilog.Sinks.Fileはファイル出力用、Serilog.Settings.Configurationはappsettings.jsonからSerilog設定を読み込むために使います。
5-3. Program.csでSerilogを設定する
ASP.NET CoreのProgram.csでSerilogを設定する例です。
C#using Serilog;
var builder = WebApplication.CreateBuilder(args);
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.WriteTo.Console()
.WriteTo.File(
"logs/app-.log",
rollingInterval: RollingInterval.Day)
.CreateLogger();
builder.Host.UseSerilog();
builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers();
try
{
Log.Information("アプリケーションを開始しました");
app.Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "アプリケーションが異常終了しました");
}
finally
{
Log.CloseAndFlush();
}
この設定では、コンソールとファイルの両方にログを出力します。rollingInterval: RollingInterval.Dayを指定しているため、日付ごとにログファイルが作成されます。
5-4. appsettings.jsonでSerilogを設定する
設定をコードに直接書くのではなく、appsettings.jsonに記述することもできます。
JSON{
"Serilog": {
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"Microsoft.AspNetCore": "Warning"
}
},
"WriteTo": [
{
"Name": "Console"
},
{
"Name": "File",
"Args": {
"path": "logs/app-.log",
"rollingInterval": "Day",
"retainedFileCountLimit": 30,
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}"
}
}
]
}
}
Program.csでは、この設定を読み込むようにします。
C#using Serilog;
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseSerilog((context, configuration) =>
{
configuration.ReadFrom.Configuration(context.Configuration);
});
builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers();
app.Run();
設定をappsettings.jsonに分けると、開発環境と本番環境でログレベルや出力先を切り替えやすくなります。
5-5. ログを日付ごとのファイルに保存する方法
Serilogで日付ごとのログファイルを作成するには、rollingIntervalを指定します。
C#.WriteTo.File(
"logs/app-.log",
rollingInterval: RollingInterval.Day)
この設定では、たとえば次のようなログファイルが作成されます。
logs/app-20260618.log
logs/app-20260619.log
logs/app-20260620.log
日付単位で分かれていると、障害が発生した日のログを探しやすくなります。
また、ログが多いアプリケーションでは、サイズによる分割も検討します。
C#.WriteTo.File(
"logs/app-.log",
rollingInterval: RollingInterval.Day,
fileSizeLimitBytes: 10_000_000,
rollOnFileSizeLimit: true)
この例では、ログファイルが一定サイズに達した場合にも新しいファイルへ切り替えます。
5-6. 出力テンプレートをカスタマイズする方法
Serilogでは、outputTemplateを使ってログの出力形式を変更できます。
C#.WriteTo.File(
"logs/app-.log",
rollingInterval: RollingInterval.Day,
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level:u3}] {SourceContext} {Message:lj}{NewLine}{Exception}")
出力例は次のようになります。
2026-06-18 10:15:30.123 [INF] MyApp.Services.UserService ユーザー情報を取得しました
よく使う項目は次のとおりです。
{Timestamp} ログ出力日時
{Level} ログレベル
{Message} ログメッセージ
{Exception} 例外情報
{SourceContext} ログカテゴリ
{NewLine} 改行
出力テンプレートを統一しておくと、ログの検索や解析がしやすくなります。
5-7. 実行してログファイルを確認する
Serilogの設定が完了したら、アプリケーションを実行してログファイルを確認します。
次のようなコードでログを出力します。
C#app.MapGet("/", (ILogger<Program> logger) =>
{
logger.LogInformation("トップページにアクセスされました");
return "Hello";
});
アプリケーションを起動し、ブラウザでアクセスすると、logsフォルダにログファイルが作成されます。
logs/app-20260618.log
ファイルの中に次のようなログが出ていれば成功です。
2026-06-18 10:15:30.123 [INF] トップページにアクセスされました
ログファイルが作成されない場合は、パッケージの追加、UseSerilogの設定、ファイル保存先の権限、ログレベルの設定を確認しましょう。
6. ログレベルの使い分けと実践例
6-1. Trace・Debug・Informationの使い分け
Trace、Debug、Informationは、主に正常系の処理を記録するときに使います。
Traceは非常に詳細なログです。ループ内の細かい状態や、メソッド内部の細かな分岐確認などに使います。ただし、出力量が多くなりやすいため、本番環境では通常無効にします。
C#_logger.LogTrace("計算処理を開始します。input={Input}", input);
Debugは、開発中の調査に使います。変数の中身や処理の分岐確認に向いています。
C#_logger.LogDebug("取得したユーザー数: {UserCount}", users.Count);
Informationは、通常の処理の流れを記録するレベルです。処理開始、処理終了、重要な状態変化などに使います。
C#_logger.LogInformation("注文処理を開始しました。OrderId={OrderId}", orderId);
本番環境で残す通常ログは、Informationを基準に考えるとよいでしょう。
6-2. Warning・Error・Criticalの使い分け
Warning、Error、Criticalは、問題が発生したときに使います。
Warningは、処理は継続できるが注意が必要な状態です。たとえば、設定値が未指定だったため既定値を使った場合や、外部APIの応答が遅い場合などです。
C#_logger.LogWarning("設定値が未指定のため既定値を使用します。Key={Key}", key);
Errorは、処理の一部が失敗した場合に使います。例外が発生してリクエスト処理に失敗した場合などが該当します。
C#_logger.LogError(ex, "注文処理に失敗しました。OrderId={OrderId}", orderId);
Criticalは、アプリケーション全体に影響する重大な障害に使います。アプリケーションが起動できない、重要な依存サービスに接続できない、といったケースです。
C#_logger.LogCritical(ex, "データベースに接続できません。アプリケーションを終了します");
ログレベルを適切に使い分けることで、運用中に重要なログを見つけやすくなります。
6-3. 例外情報をログに出力する方法
例外をログに出力するときは、例外オブジェクトをログメソッドの第1引数に渡します。
C#try
{
Execute();
}
catch (Exception ex)
{
_logger.LogError(ex, "処理中に例外が発生しました");
}
このように書くと、例外メッセージだけでなく、スタックトレースもログに出力できます。
避けたい書き方は、例外メッセージだけを文字列として出す方法です。
C#_logger.LogError("エラー: " + ex.Message);
この書き方だと、スタックトレースなどの詳細情報が失われ、原因調査が難しくなります。
例外ログでは、必ず例外オブジェクトを渡すようにしましょう。
6-4. 変数や処理結果をログに含める方法
C#のログ出力では、文字列補間よりもプレースホルダーを使う書き方がおすすめです。
C#_logger.LogInformation("ユーザーを取得しました。UserId={UserId}", userId);
この書き方には、次のようなメリットがあります。
・構造化ログとして扱いやすい
・検索や分析がしやすい
・ログレベルによって出力されない場合の無駄な文字列生成を避けやすい
複数の値を含める場合は、次のように書きます。
C#_logger.LogInformation(
"注文処理が完了しました。OrderId={OrderId}, Amount={Amount}, UserId={UserId}",
orderId,
amount,
userId);
ログを読む人が状況を理解しやすいように、識別子や処理結果を適切に含めることが大切です。
6-5. 本番環境で出力すべきログレベル
本番環境では、一般的にInformation以上を出力する構成がよく使われます。
JSON{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
ただし、アクセス数が多いサービスではInformationでもログ量が多くなることがあります。その場合は、通常時はWarning以上にし、調査時だけ一時的にDebugやInformationを有効にする運用もあります。
本番環境でTraceやDebugを常時出すのは避けた方が安全です。ログ量が膨大になり、ディスク容量やログ収集コストを圧迫する可能性があります。
また、詳細なログには個人情報や内部情報が含まれやすいため、本番環境では出力内容にも注意が必要です。
7. C#ログ出力の実践コード例
7-1. コンソールアプリでログを出力するサンプル
コンソールアプリでILoggerを使うサンプルです。
C#using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using IHost 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("処理を開始しました");
for (int i = 1; i <= 3; i++)
{
_logger.LogInformation("処理中です。Count={Count}", i);
}
_logger.LogInformation("処理を終了しました");
}
}
このコードでは、Generic Hostを使ってDIとログ機能を利用しています。小さなバッチ処理でも、この構成にしておくと拡張しやすくなります。
7-2. ASP.NET Coreでリクエスト処理をログ出力するサンプル
ASP.NET CoreのMinimal APIでログを出力する例です。
C#var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.MapGet("/users/{id}", (int id, ILogger<Program> logger) =>
{
logger.LogInformation("ユーザー取得リクエストを受け付けました。UserId={UserId}", id);
if (id <= 0)
{
logger.LogWarning("不正なUserIdが指定されました。UserId={UserId}", id);
return Results.BadRequest("UserId is invalid.");
}
logger.LogInformation("ユーザー取得処理が完了しました。UserId={UserId}", id);
return Results.Ok(new { Id = id, Name = "Sample User" });
});
app.Run();
リクエストを受け付けたタイミング、不正な入力を検出したタイミング、処理が完了したタイミングでログを出しています。
Webアプリでは、リクエストID、ユーザーID、対象データのIDなどをログに含めると、障害調査がしやすくなります。
7-3. try-catchで例外ログを出力するサンプル
例外発生時にログを残すサンプルです。
C#public class OrderService
{
private readonly ILogger<OrderService> _logger;
public OrderService(ILogger<OrderService> logger)
{
_logger = logger;
}
public void CreateOrder(int orderId)
{
try
{
_logger.LogInformation("注文作成を開始しました。OrderId={OrderId}", orderId);
// 注文作成処理
throw new InvalidOperationException("在庫が不足しています");
}
catch (Exception ex)
{
_logger.LogError(ex, "注文作成に失敗しました。OrderId={OrderId}", orderId);
throw;
}
}
}
catch内でログを出したあと、throw;で再スローしています。これにより、呼び出し元でも適切にエラー処理を続けられます。
注意点として、同じ例外を複数箇所で何度もログ出力すると、ログが重複して読みにくくなります。どの階層で例外ログを出すかは、アプリケーション全体で方針を決めておくとよいです。
7-4. Serilogでファイル保存する完全サンプル
ASP.NET CoreでSerilogを使い、ログをコンソールとファイルに出力する完全サンプルです。
C#using Serilog;
var builder = WebApplication.CreateBuilder(args);
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Information()
.Enrich.FromLogContext()
.WriteTo.Console()
.WriteTo.File(
path: "logs/app-.log",
rollingInterval: RollingInterval.Day,
retainedFileCountLimit: 30,
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level:u3}] {SourceContext} {Message:lj}{NewLine}{Exception}")
.CreateLogger();
builder.Host.UseSerilog();
builder.Services.AddControllers();
var app = builder.Build();
app.MapGet("/", (ILogger<Program> logger) =>
{
logger.LogInformation("トップページにアクセスされました");
return Results.Ok("Hello Serilog");
});
try
{
Log.Information("アプリケーションを開始しました");
app.Run();
}
catch (Exception ex)
{
Log.Fatal(ex, "アプリケーションが異常終了しました");
}
finally
{
Log.CloseAndFlush();
}
このサンプルでは、logsフォルダに日付ごとのログファイルが作成されます。retainedFileCountLimitにより、保持するログファイル数も制限しています。
7-5. appsettings.jsonを使った設定サンプル
Serilogの設定をappsettings.jsonにまとめる例です。
JSON{
"Serilog": {
"Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ],
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"Microsoft.AspNetCore": "Warning",
"System": "Warning"
}
},
"Enrich": [ "FromLogContext" ],
"WriteTo": [
{
"Name": "Console"
},
{
"Name": "File",
"Args": {
"path": "logs/app-.log",
"rollingInterval": "Day",
"retainedFileCountLimit": 30,
"fileSizeLimitBytes": 10485760,
"rollOnFileSizeLimit": true,
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff} [{Level:u3}] {SourceContext} {Message:lj}{NewLine}{Exception}"
}
}
]
}
}
Program.csは次のようにします。
C#using Serilog;
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseSerilog((context, services, configuration) =>
{
configuration
.ReadFrom.Configuration(context.Configuration)
.ReadFrom.Services(services)
.Enrich.FromLogContext();
});
var app = builder.Build();
app.MapGet("/", (ILogger<Program> logger) =>
{
logger.LogInformation("アプリケーションにアクセスされました");
return "OK";
});
app.Run();
設定を外部ファイルに出すことで、環境ごとの切り替えが簡単になります。
8. C#ログ出力でよくあるエラーと対処法
8-1. ログがコンソールに表示されない
ログがコンソールに表示されない場合、まずログレベルを確認します。
たとえば、appsettings.jsonで次のように設定されていると、Informationログは出力されません。
JSON{
"Logging": {
"LogLevel": {
"Default": "Warning"
}
}
}
この場合、次のログは表示されません。
C#_logger.LogInformation("このログは表示されません");
表示したい場合は、ログレベルをInformationに変更します。
JSON{
"Logging": {
"LogLevel": {
"Default": "Information"
}
}
}
また、Serilogを使っている場合は、.WriteTo.Console()が設定されているか確認してください。
8-2. ログファイルが作成されない
Serilogでログファイルが作成されない場合、次の点を確認します。
・Serilog.Sinks.Fileをインストールしているか
・WriteTo.Fileの設定があるか
・UseSerilogを呼び出しているか
・ログレベルで出力が抑制されていないか
・保存先フォルダに書き込み権限があるか
・相対パスの基準位置を誤解していないか
特に多いのは、保存先の権限不足です。WindowsサービスやIIS上で動かす場合、実行ユーザーがログフォルダに書き込める必要があります。
また、相対パスを使っている場合、想定とは違う場所にログファイルが作成されていることもあります。必要に応じて絶対パスで確認しましょう。
8-3. appsettings.jsonの設定が反映されない
appsettings.jsonの設定が反映されない場合は、Serilogの設定読み込みができているか確認します。
C#builder.Host.UseSerilog((context, configuration) =>
{
configuration.ReadFrom.Configuration(context.Configuration);
});
また、Serilog.Settings.Configurationパッケージが入っていないと、appsettings.jsonからSerilog設定を読み込めません。
環境別設定ファイルも確認しましょう。ASP.NET Coreでは、環境によってappsettings.Development.jsonなどが読み込まれます。開発環境で別のログ設定が上書きされている可能性があります。
8-4. ログレベルを変更しても出力内容が変わらない
ログレベルを変更しても出力内容が変わらない場合、複数の設定が競合している可能性があります。
たとえば、標準のLogging設定とSerilogのMinimumLevel設定を両方書いている場合、実際にはSerilog側の設定が効いていることがあります。
Serilogを使う場合は、次のような設定を確認します。
JSON{
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft.AspNetCore": "Warning"
}
}
}
}
また、カテゴリ別の設定にも注意が必要です。DefaultではInformationでも、特定カテゴリがWarningに設定されていれば、そのカテゴリのInformationログは出力されません。
8-5. ファイルの保存先や権限で失敗する
ログファイル出力では、保存先の権限エラーがよく発生します。
たとえば、次のような場所にログを出そうとすると、実行ユーザーによっては書き込めない場合があります。
C:\Program Files\MyApp\logs
C:\Windows\System32\logs
対策としては、アプリケーション専用の書き込み可能なフォルダを用意します。
C:\Logs\MyApp
D:\AppLogs\MyApp
また、Linux環境では次のようなパスを使うことがあります。
/var/log/myapp
ただし、この場合も実行ユーザーに書き込み権限が必要です。
権限エラーを避けるためには、デプロイ時にログフォルダを作成し、アプリケーションの実行ユーザーへ書き込み権限を付与しておきましょう。
9. C#ログ出力のベストプラクティス
9-1. ログに個人情報や機密情報を出さない
ログには、個人情報や機密情報を出力しないことが重要です。
出力を避けるべき情報には、次のようなものがあります。
・パスワード
・アクセストークン
・APIキー
・クレジットカード番号
・マイナンバーなどの識別番号
・住所や電話番号
・メール本文などの機密性が高い情報
ログは開発者や運用担当者が確認するだけでなく、ログ収集サービスやバックアップに保存されることもあります。ログに機密情報を出してしまうと、情報漏えいのリスクが高まります。
ユーザーIDなどが必要な場合も、個人を直接特定できる情報ではなく、内部IDやマスク済みの値を使うことを検討しましょう。
9-2. ログメッセージは後から検索しやすく書く
ログメッセージは、あとから検索しやすいように書くことが大切です。
悪い例です。
C#_logger.LogInformation("成功しました");
これだけでは、何が成功したのかわかりません。
良い例です。
C#_logger.LogInformation("注文作成に成功しました。OrderId={OrderId}", orderId);
このように、処理名と識別子を含めると、あとから検索しやすくなります。
ログメッセージでは、次の点を意識しましょう。
・何の処理かを明確にする
・対象データのIDを含める
・成功、失敗、開始、終了を区別する
・同じ形式で統一する
9-3. 構造化ログを活用する
C#でログ出力するなら、構造化ログを活用するのがおすすめです。
文字列補間ではなく、プレースホルダーを使います。
C#_logger.LogInformation("ユーザーがログインしました。UserId={UserId}", userId);
避けたい例です。
C#_logger.LogInformation($"ユーザーがログインしました。UserId={userId}");
どちらも見た目は似ていますが、構造化ログではUserIdをプロパティとして扱えます。ログ分析ツールを使うと、UserIdで絞り込んだり、集計したりしやすくなります。
Serilogを使う場合は、構造化ログのメリットを特に活かしやすいです。
9-4. 本番環境と開発環境で設定を分ける
ログ設定は、開発環境と本番環境で分けるべきです。
開発環境では、詳細なログが必要になることがあります。
JSON{
"Logging": {
"LogLevel": {
"Default": "Debug"
}
}
}
一方、本番環境ではログ量を抑え、重要なログを中心に出力します。
JSON{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}
環境別のappsettingsを使うと、設定を分けやすくなります。
appsettings.json
appsettings.Development.json
appsettings.Production.json
本番環境で詳細ログを出す場合は、一時的に有効化し、調査が終わったら元に戻す運用が安全です。
9-5. ログの肥大化を防ぐ
ログは便利ですが、出しすぎると問題になります。
ログが肥大化すると、次のような影響があります。
・ディスク容量を圧迫する
・ログ検索が遅くなる
・重要なログが埋もれる
・ログ収集サービスのコストが増える
・アプリケーションのパフォーマンスに影響する
対策としては、ログレベルを適切に設定し、日付やサイズによるローテーションを行います。
Serilogでは、次のように保持ファイル数を制限できます。
C#.WriteTo.File(
"logs/app-.log",
rollingInterval: RollingInterval.Day,
retainedFileCountLimit: 30)
また、ループ内で大量にログを出す場合は、必要性をよく検討しましょう。
9-6. 監視・分析ツールとの連携を考える
ログはファイルに保存するだけでなく、監視・分析ツールと連携するとさらに活用しやすくなります。
たとえば、ログを集中管理することで、複数サーバーのログをまとめて検索できます。エラーが増えたときにアラートを出したり、特定の処理時間を分析したりすることもできます。
クラウド環境では、標準出力にログを出し、プラットフォーム側のログ収集機能で管理する構成も一般的です。
将来的な運用を考えるなら、最初から構造化ログを意識しておくと、ログ分析ツールに移行しやすくなります。
10. C#のログ出力に関するよくある質問
10-1. C#でログ出力するならILoggerとSerilogのどちらを使うべき?
基本的には、アプリケーションコードではILoggerを使い、出力先としてSerilogを組み合わせるのがおすすめです。
ILoggerは.NET標準のログ抽象化であり、ASP.NET Coreとの相性がよいです。一方、Serilogはファイル出力や構造化ログ、出力先の拡張に強みがあります。
つまり、どちらか一方を選ぶというより、ILoggerでログを書き、Serilogで出力を強化する構成が実践的です。
10-2. Console.WriteLineを本番環境で使ってもよい?
小さなコンソールアプリや一時的な確認であればConsole.WriteLineでも問題ない場合があります。
ただし、本番環境のアプリケーションでは、ILoggerなどのログライブラリを使う方が適しています。ログレベルの制御、出力先の変更、例外情報の記録、構造化ログなどが使えるためです。
特にWebアプリ、バッチ処理、Windowsサービスでは、Console.WriteLineだけに頼らず、ILoggerを使うことをおすすめします。
10-3. ログファイルはどこに保存するのがよい?
ログファイルは、アプリケーションの実行ユーザーが書き込める場所に保存します。
開発中は、プロジェクト配下のlogsフォルダで問題ありません。
logs/app-.log
本番環境では、専用のログフォルダを用意するのが安全です。
C:\Logs\MyApp
/var/log/myapp
ただし、IIS、Windowsサービス、Docker、クラウド環境では、推奨されるログ管理方法が異なります。ファイル保存よりも標準出力やログ収集サービスを使う方が適している場合もあります。
10-4. ログレベルはどのように決めればよい?
ログレベルは、運用時にどれだけ重要な情報かで決めます。
通常の処理開始や完了はInformation、開発中の詳細確認はDebug、注意が必要な状態はWarning、処理失敗はError、システム全体に影響する障害はCriticalを使います。
本番環境では、まずInformation以上を基準にし、ログ量が多ければWarning以上に絞ることを検討します。
重要なのは、チーム内でログレベルの基準を統一することです。
10-5. C#でJSON形式のログを出力できる?
C#では、Serilogを使ってJSON形式のログを出力できます。
たとえば、Serilog.Formatting.Compactを使うと、コンパクトなJSONログを出力できます。
Bashdotnet add package Serilog.Formatting.Compact
設定例です。
C#using Serilog;
using Serilog.Formatting.Compact;
Log.Logger = new LoggerConfiguration()
.WriteTo.File(
new CompactJsonFormatter(),
"logs/app-.json",
rollingInterval: RollingInterval.Day)
.CreateLogger();
JSON形式のログは、ログ分析ツールやクラウドログ基盤に取り込む場合に便利です。
10-6. Windowsサービスやバッチ処理でも同じ方法で使える?
Windowsサービスやバッチ処理でも、ILoggerやSerilogを使えます。
特に、Generic Hostを使ってアプリケーションを構成すると、DI、設定ファイル、ログ出力を統一的に扱えます。
C#using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using IHost host = Host.CreateDefaultBuilder(args)
.UseWindowsService()
.ConfigureServices(services =>
{
services.AddHostedService<Worker>();
})
.Build();
host.Run();
Windowsサービスやバッチ処理は画面を持たないことが多いため、ログの重要性が高くなります。処理開始、終了、件数、例外、外部連携の結果などを適切に記録しましょう。
まとめ
C#のログ出力は、アプリケーションの開発と運用に欠かせない仕組みです。簡単な確認であればConsole.WriteLineでも対応できますが、実務ではILoggerを使ってログレベルや出力形式を管理するのが基本です。
ASP.NET CoreではILoggerが標準的に使えるため、コントローラーやサービスに注入するだけでログ出力を始められます。コンソールアプリやバッチ処理でも、Generic Hostを使えば同じようにILoggerを利用できます。
ログをファイルに保存したい場合は、Serilogを組み合わせる方法が実践的です。Serilog.Sinks.Fileを使えば、日付ごとのログファイル作成、ログローテーション、出力テンプレートのカスタマイズなどを簡単に実装できます。
ログ出力では、ログレベルの使い分けも重要です。通常の処理はInformation、注意が必要な状態はWarning、処理失敗はError、重大障害はCriticalというように、基準を決めて運用しましょう。
また、ログには個人情報や機密情報を出さないこと、検索しやすいメッセージにすること、構造化ログを活用することも大切です。
C#でログ出力を実装するなら、まずはILoggerで基本を押さえ、必要に応じてSerilogでファイル保存や構造化ログを強化していくのがおすすめです。

