Skip to main content

第11章:ログの基礎(困った時に追えるように)🧾🔍

この章のゴール🎯

ログを「なんとなく出す」から卒業して、原因にたどり着けるログを作れるようになります💪💖 リファクタ中に「あれ、動き変わってない…?」って不安になった時も、ログがあると安心です🙂🛡️


1. ログって結局なに?🤔📝

ログは、アプリの動きをあとから追いかけるための「足あと」👣✨ 特に、次みたいなときに強い味方になります💡

  • 失敗したのは どこ?(どの処理の途中?)🔎
  • 何が 原因?(入力?外部API?DB?)🧩
  • 再現しない不具合が 本番だけ で起きる…😱(←ログがないと詰みがち)

.NET のログは ILogger を中心に「高性能・構造化ログ」を前提に作られています📦✨ (Microsoft Learn)


2. ログレベル(重要度)を使い分けよう🚦✨

ログは「重要度」で分類します。これを ログレベル と呼びます🙂

  • Trace:いちばん細かい(情報量が多すぎ注意)🧠💥 ※機密情報が混ざりやすいので、本番では基本ONにしない想定です⚠️ (Microsoft Learn)
  • Debug:開発中の調査向け🔧
  • Information:通常の流れを追うための記録📍
  • Warning:変だけど致命的ではない(例:リトライした、想定外入力だけど処理継続)🟡
  • Error:処理が失敗した(ただしアプリ全体が落ちたとは限らない)🔴
  • Critical:致命的(サービス継続が難しい)💣

このレベル分けがあるから、必要なログだけを出す(ノイズを減らす)ことができます✨ (Microsoft Learn)


3. 「ログは文章じゃなくデータ」ってどういうこと?📦🪄

C# の ILogger は「構造化ログ」が基本です。 ポイントは メッセージテンプレート{} のプレースホルダー)🧩✨

たとえば、こう👇

logger.LogInformation("Payment started. UserId={UserId} Amount={Amount}", userId, amount);

この {UserId}{Amount} は、ただの文字埋め込みじゃなくて “フィールド(検索できるデータ)” として扱える設計です📌✨ (Microsoft Learn)

文字列補間($"...")はなるべく避けよう🙅‍♀️⚡

ログで $"UserId={userId}" みたいな文字列補間を多用すると、性能面で不利になることがあります⚠️ Microsoft のドキュメントでも「多くの場合テンプレート形式を推奨」とされています✨ (Microsoft Learn)


4. “1行の情報量”の作り方📏🧾✨

読みやすいログは、1行で状況が分かるのが理想です🌟

最低限おすすめはこのセット👇(全部を毎回入れる必要はないよ🙂)

  • 何が起きた?(短い動詞で)📝
  • どの対象?(例:注文ID、ユーザーID、ファイル名)🏷️
  • 結果は?(成功/失敗、件数、ステータス)✅❌
  • 失敗なら例外も(ex を渡す)💥
  • 追跡用ID(あとで追える)🧵✨(後で出てくる Scope が便利!)

5. どこにログが出るの?(代表)🪟🔍

.NET のログは「プロバイダー」を追加して、出力先を決めます📮✨ 標準で使える代表例はこんな感じ👇

  • Console:コンソール出力🖥️
  • Debug:デバッグ出力(Visual Studio の Output ウィンドウなど)🧠
  • EventSource:イベントトレース系(Windows なら ETW と関係)📡
  • EventLog(Windows):Windowsイベントログ📘

テンプレート(Worker系など)では CreateApplicationBuilder がこれらを追加するのが一般的です🧰✨ (Microsoft Learn)

ファイルに出したいときは?📄

ここ、超大事ポイント⚠️ ASP.NET Core には “ファイルへ書く標準プロバイダー” は含まれていません。 ファイル出力したいなら サードパーティ(例:Serilog) を使うのが定番です📌 (Microsoft Learn)


6. 実装:最小のログを出してみよう🛠️✨

6-1. まずは ILogger を使う(Consoleアプリ例)🖥️🧪

「ホストを作って、ILoggerを注入して使う」形が基本です🙂

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

var builder = Host.CreateApplicationBuilder(args);

// 出力先を分かりやすく(コンソールに)
builder.Logging.ClearProviders();
builder.Logging.AddSimpleConsole(options =>
{
options.SingleLine = true;
options.TimestampFormat = "HH:mm:ss ";
});

builder.Logging.SetMinimumLevel(LogLevel.Information);

builder.Services.AddTransient<CheckoutService>();

using var host = builder.Build();

var svc = host.Services.GetRequiredService<CheckoutService>();
await svc.CheckoutAsync(userId: "u-123", amount: 2980);

public sealed class CheckoutService(ILogger<CheckoutService> logger)
{
public Task CheckoutAsync(string userId, int amount)
{
logger.LogInformation("Checkout started. UserId={UserId} Amount={Amount}", userId, amount);
logger.LogInformation("Checkout finished. UserId={UserId}", userId);
return Task.CompletedTask;
}
}

コツ:ログ本文は短く! 代わりに {UserId} みたいなフィールドを添えると、調査が超ラクになります📌✨ (Microsoft Learn)


7. EventId(イベント番号)で「同じ種類のログ」を束ねる🏷️🔢✨

ログが増えると、検索で「この種類のログだけ見たい!」が必ず来ます👀 そこで便利なのが EventId です💡

using Microsoft.Extensions.Logging;

internal static class LogEvents
{
internal static readonly EventId CheckoutStart = new(1000, "CheckoutStart");
internal static readonly EventId CheckoutEnd = new(1001, "CheckoutEnd");
internal static readonly EventId CheckoutFail = new(1400, "CheckoutFail");
}

使う側👇

logger.LogInformation(LogEvents.CheckoutStart, "Checkout started. UserId={UserId} Amount={Amount}", userId, amount);

EventId は「同じ種類の出来事」をまとめる考え方です🧩✨ (Microsoft Learn)


8. Scope(スコープ)で “共通情報” を自動で付ける🧵✨

リファクタや不具合調査で「この一連の処理のログだけ追いたい!」ってなりがちです🙂 そこで Scope を使うと、同じ処理中のログに共通情報を付けられます📌

using System.Collections.Generic;

var traceId = Guid.NewGuid().ToString("N");

using (logger.BeginScope(new List<KeyValuePair<string, object>>
{
new("TraceId", traceId),
new("UserId", userId),
}))
{
logger.LogInformation("Checkout started. Amount={Amount}", amount);
logger.LogInformation("Validate finished");
logger.LogInformation("Checkout finished");
}

Scope は「同じデータをまとめて付ける」ための仕組みで、BeginScope() が返す IDisposableusing で囲みます🧤✨ (Microsoft Learn)


9. 例外は “exを渡して” 記録する💥🧯

エラーのログは、例外オブジェクトも一緒に渡すのが基本です🙂 スタックトレースなどが出力先に残せます📌(出力のされ方はプロバイダー依存) (Microsoft Learn)

try
{
DoSomething();
}
catch (Exception ex)
{
logger.LogError(LogEvents.CheckoutFail, ex, "Checkout failed. UserId={UserId}", userId);
throw;
}

10. ミニ演習:失敗ケースのログを読みやすくする🪄📝✨

お題🎀

次の「微妙ログ」を、原因に近づけるログに直してみよう💪

❌ よくある微妙パターン(情報が足りない / 検索しづらい)😵‍💫

Console.WriteLine("start");
Console.WriteLine("error!");
Console.WriteLine("end");

✅ 改善の方向性(例)🌟

  • レベルを付ける(Information / Warning / Error)🚦
  • 何の処理か分かる動詞にする📝
  • 重要な値をフィールド化する({OrderId} みたいに)🏷️
  • 失敗時は例外も付ける💥
  • 1つの処理を Scope で束ねる🧵

目標イメージ(サンプル)✨

var traceId = Guid.NewGuid().ToString("N");

using (logger.BeginScope(new[] { new KeyValuePair<string, object>("TraceId", traceId) }))
{
logger.LogInformation("Payment started. OrderId={OrderId} Amount={Amount}", orderId, amount);

try
{
gateway.Charge(orderId, amount);
logger.LogInformation("Payment succeeded. OrderId={OrderId}", orderId);
}
catch (Exception ex)
{
logger.LogError(ex, "Payment failed. OrderId={OrderId} Amount={Amount}", orderId, amount);
throw;
}
}

11. AI拡張(Copilot / OpenAI系)に頼むときのコツ🤖✨

AIにログ改善を頼むと、スピードは出ます⚡ でも「入れるべき情報」は仕様や安全面に直結するので、人間が最終判断します🙂🛡️

使いやすい依頼文(例)🧾

  • 「このメソッドで、調査に必要なログ項目(フィールド)を列挙して」📋
  • 「Information/Warning/Error の境界を提案して(理由つきで)」🚦
  • 「メッセージテンプレート形式({})で、ログ案を3パターン」🧩
  • 「PII/秘密情報をログに入れないように、危険な項目を指摘して」⚠️

レビューの観点✅

  • 文字列補間 $"..." になってない?(テンプレートが良い) (Microsoft Learn)
  • {} の中身が「意味ある名前」になってる?🏷️
  • 失敗時に、必要な値が残る?(OrderId/外部結果など)🧠
  • 機密が混ざってない?(トークン/パスワード/個人情報)🔒

12. 仕上げチェックリスト✅🧾✨

コミット前に、ここだけ確認すると事故りにくいよ🙂

  • 成功ログ:処理の開始/完了が追える📍
  • 失敗ログ:例外(ex)+原因に近い値(ID等)がある💥
  • テンプレート形式で出してる(補間しない)🧩 (Microsoft Learn)
  • ログレベルが適切(全部Errorにしてない)🚦 (Microsoft Learn)
  • 重要処理は Scope で束ねられる(TraceIdなど)🧵 (Microsoft Learn)

13. (ちょい先取り)観測の世界:OpenTelemetryって?🌍✨

「ログを集めて検索しやすくする」「トレースと一緒に見る」みたいな“観測”の仕組みとして、OpenTelemetry がよく使われます📡 OpenTelemetry .NET は ILogger と統合してログを収集・エクスポートできる、と説明されています🧩✨ (OpenTelemetry)

今すぐ必須ではないけど、「ログはデータ」「TraceIdで束ねる」みたいな考え方が、ここに繋がっていきます🙂🧵


ここまでで、ログは「出す」じゃなくて “追える形で残す” ものになってきたはず!🧾✨ 次の章(例外)に入ると、失敗ログの品質がさらに上がります💥📈