はじめに:「ログ出力」は“未来の自分へのメッセージ”
業務システムで一番「効いてくる」のがログです。
動いているときは意識されないのに、トラブルが起きた瞬間に「ログがすべての命綱」になります。
何が起きたのか
どの入力で
どの処理の途中で
どんな例外が出たのか
これを後から追えるかどうかは、「ログをどう設計しているか」でほぼ決まります。
ここでは、初心者向けに
なぜログが必要なのか
ログレベル(情報・警告・エラー)の考え方
実務でよく使うログ出力のパターン
ILogger を使ったシンプルなユーティリティ化
を、例と一緒にかみ砕いて説明していきます。
ログ出力の目的をはっきりさせる
「今のため」ではなく「後から調べるため」
ログの一番大事な役割は、「後から状況を再現する手がかり」です。
つまり、ログは「未来の自分(やチームメンバー)への説明書」です。
ユーザーから「たまに画面が固まる」と言われた
バッチが夜中に落ちていた
API がたまにタイムアウトする
こういうときに、ログがなければ「再現できません」で終わってしまいます。
ログがあれば、
何時何分に
どのユーザーが
どの画面で
どんな入力をして
どの処理でエラーになったか
を追いかけることができます。
ここでの重要ポイントは、「ログは“今の表示”ではなく、“後から読む文章”だと意識して書く」ことです。
「自分が他人のログを読む」と想像すると、何を書けばいいかが見えてきます。
ログレベルの基本イメージ
情報・警告・エラーをざっくり分ける
多くのログライブラリには、こんなレベルがあります。
Information(情報)
Warning(警告)
Error(エラー)
Debug / Trace(開発・詳細用)
ざっくりした使い分けのイメージはこうです。
Information
正常系の重要な出来事(処理開始・終了、重要な状態変化など)
Warning
今は動いているが、将来問題になりそうな状態(リトライで復旧した、想定外の入力だが処理は続行したなど)
Error
処理が失敗した、ユーザーにエラーを返した
Debug / Trace は「開発中や調査中にだけ有効にする詳細ログ」と考えるとよいです。
ここでの重要ポイントは、「全部 Error にしない、全部 Information にもしない」ことです。
レベルを分けておくことで、「本番で見るログ」「調査するときだけ見るログ」を切り替えられます。
.NET の ILogger を使った基本的なログ出力
ILogger の注入と基本メソッド
ASP.NET Core などでは、ILogger<T> が標準的なログインターフェースです。
クラスにこう書きます。
using Microsoft.Extensions.Logging;
public class UserService
{
private readonly ILogger<UserService> _logger;
public UserService(ILogger<UserService> logger)
{
_logger = logger;
}
public void CreateUser(string name)
{
_logger.LogInformation("ユーザー作成開始 Name={Name}", name);
// 何か処理
}
}
C#LogInformation の第1引数はメッセージテンプレート、第2引数以降が埋め込む値です。Name={Name} のように書いておくと、構造化ログとして扱えるので、後から検索しやすくなります。
ここでの重要ポイントは、「文字列連結ではなく“テンプレート+名前付きパラメータ”で書く」ことです。"Name=" + name ではなく、"Name={Name}" と書く癖をつけると、ログ分析ツールとの相性が良くなります。
例1:処理の開始・終了をログに残す
「どこで止まっているか」を追えるようにする
例えば、ユーザー登録処理を考えます。
public async Task RegisterAsync(UserInput input)
{
_logger.LogInformation("ユーザー登録開始 Email={Email}", input.Email);
// バリデーション
// DB 登録
// メール送信 など
_logger.LogInformation("ユーザー登録完了 Email={Email}", input.Email);
}
C#これだけでも、
いつ
どのメールアドレスで
登録処理が開始され
正常に完了したか
が分かります。
もし途中で例外が出た場合、
「開始ログはあるが完了ログがない」状態になるので、
「どの処理で止まっているか」を推測する手がかりになります。
ここでの重要ポイントは、「長めの処理には“開始”と“終了”のログをセットで入れる」ことです。
これだけで、ログの読みやすさが一気に変わります。
例2:例外発生時のログ出力
例外は「メッセージ+スタックトレース」を必ず残す
例外が起きたときは、必ずログに残すべきです。ILogger には、例外を一緒に渡せるオーバーロードがあります。
try
{
await _mailSender.SendAsync(to, subject, body);
}
catch (Exception ex)
{
_logger.LogError(ex,
"メール送信に失敗しました To={To}, Subject={Subject}",
to, subject);
throw; // 上位に再スローするか、適切にハンドリングする
}
C#LogError(ex, "メッセージ", ...) と書くことで、
エラーメッセージ
スタックトレース
パラメータ(To, Subject など)
がまとめてログに残ります。
ここでの重要ポイントは、「例外をログに書くときは、Exception オブジェクトを必ず渡す」ことです。
メッセージだけ書いても、どこで落ちたかが分からなくなります。
例3:業務的なエラーと技術的なエラーを分けてログに残す
「ユーザーの入力ミス」と「システムのバグ」は別物
例えば、ユーザーが存在しない ID を指定した場合。
public async Task<User> GetUserAsync(int id)
{
var user = await _userRepository.FindAsync(id);
if (user == null)
{
_logger.LogWarning("ユーザーが見つかりませんでした Id={Id}", id);
throw new UserNotFoundException(id);
}
return user;
}
C#これは「想定内のエラー」です。
システムのバグではなく、「そういうケースもあり得る」状況なので、
Warning レベルでログに残すのがちょうどよいです。
一方、NullReferenceException や DB 接続エラーなどは、
「想定外の技術的なエラー」なので、Error レベルで記録します。
ここでの重要ポイントは、「“業務的にあり得るエラー”と“システムの異常”をログレベルで分ける」ことです。
本番運用では、Error を最優先で監視し、Warning を状況確認に使う、という運用がしやすくなります。
例4:パフォーマンス計測用のログ
「どこが遅いのか」を後から測れるようにする
処理時間をログに出しておくと、
「なんとなく遅い」を「どこが何ミリ秒かかっている」に変えられます。
public async Task ProcessAsync()
{
var sw = System.Diagnostics.Stopwatch.StartNew();
_logger.LogInformation("処理開始");
await Step1Async();
await Step2Async();
sw.Stop();
_logger.LogInformation("処理完了 ElapsedMs={ElapsedMs}", sw.ElapsedMilliseconds);
}
C#これで、「処理完了」のログを集計すれば、
平均処理時間や、たまに極端に遅いケースなどを分析できます。
ここでの重要ポイントは、「“遅い”と感じたら、まずログで時間を計測する癖をつける」ことです。
感覚ではなく数字で語れるようになります。
ログ出力ユーティリティを用意する
「毎回同じ書き方をしない」ための薄いラッパー
プロジェクトが大きくなると、
「ログメッセージの書き方」「パラメータの付け方」を統一したくなります。
例えば、「操作ログ」を専用メソッドにまとめるイメージです。
public static class AppLog
{
public static void Operation(ILogger logger, string operation, string userId, object? detail = null)
{
logger.LogInformation(
"Operation={Operation}, User={User}, Detail={Detail}",
operation, userId, detail);
}
}
C#使う側はこう書けます。
AppLog.Operation(_logger, "UserCreate", currentUserId, new { Email = input.Email });
C#ここでの重要ポイントは、「“よく出すログ”には名前をつけてユーティリティ化する」ことです。
ログの粒度やフォーマットが揃い、後から分析しやすくなります。
ログに「書かないほうがいいもの」も意識する
個人情報・機密情報は慎重に
ログは便利ですが、「何でもかんでも書いていい」わけではありません。
パスワード
クレジットカード番号
個人を特定できる情報の詳細(住所・電話番号など)
こういったものは、原則としてログに書かない、もしくはマスクするべきです。
_logger.LogInformation("ログイン試行 Username={Username}", input.Username);
// Password は絶対にログに出さない
C#ここでの重要ポイントは、「“ログは長期間残ることが多い=漏れたときの影響が大きい”」ということです。
「この情報がログに残っていても大丈夫か?」を一度立ち止まって考える癖をつけましょう。
まとめ:「ログ出力ユーティリティ」は“未来の自分を助けるための仕組み”
ログ出力の本質は、
後からトラブルを調査するときに、
「何が起きたか」を再現できるだけの情報を、
適切なレベルとフォーマットで残しておくこと
です。
押さえておきたいポイントを整理すると、
ログは「未来の自分への説明書」だと意識して書く
Information / Warning / Error を使い分けて、重要度を表現する
処理の開始・終了、例外発生時、パフォーマンス計測などは特にログが効く
ILogger のメッセージテンプレート(Name={Name})を使うと、構造化ログになって分析しやすい
よく使うログはユーティリティ化して、粒度とフォーマットを揃える
書いてはいけない情報(パスワードなど)も意識してフィルタする
ここまで腹落ちしていれば、
「とりあえず Console.WriteLine」から卒業して、
“運用で本当に役に立つログ”を設計できるエンジニアに近づけます。

