logging について
.NET アプリケーションのログと可観測性を扱うスキルです。ヘルスエンドポイントや相関 ID、ログレベルの方針を説明し、Serilog と OpenTelemetry の設定は各専用スキルに委ねます。
主な機能
- 01稼働確認と準備状態のヘルスエンドポイント
- 02リクエスト処理を通じた相関 ID の伝播
- 03環境別のログレベル方針
- 04ログへの機密データ記録の回避
活用シーン
- 01サービスへのヘルスチェック追加
- 02下流呼び出しをまたぐリクエスト追跡
- 03環境に応じたログレベルの選択
インストール
コマンドをコピーしてターミナルで実行します。
$
npx skills add https:/ / github.com/ codewithmukesh/ dotnet-claude-kit --skill loggingインストール後、Claude Code で/loggingを入力して読み込まれたか確認します。
詳細
ログ記録と可観測性
基本原則
- Serilog による構造化ログ記録 — すべてのログエントリは、書式化された文字列ではなく、名前付きプロパティを持つ構造化イベントです。これにより、検索、フィルタリング、アラートが可能になります。二段階ブートストラップ、
AddSerilog()、シンク、エンリッチャーを含むすべての設定は serilog スキルにあります。このスキルにあるUseSerilog()よりAddSerilog()を使うというガイダンスが正式な指針です。 - 分散トレーシングには OpenTelemetry — トレースはサービス間のリクエストをつなぎ、メトリクスはシステムの健全性を時系列で追跡します。設定の詳細は opentelemetry スキルにあります。
- 運用準備状況の確認にはヘルスチェック — すべてのサービスで、ロードバランサーやオーケストレーター向けに
/healthエンドポイントを公開します。ライブネスとレディネスは別々の問いであり、エンドポイントも分けます。 - リクエストトレーシングには相関 ID — すべてのリクエストに一意の ID を割り当て、すべてのログエントリと下流サービスへの呼び出しに引き継ぎます。これにより、ユーザーからの問い合わせを、フィルタリングした 1 つのログストリームに対応付けられます。
パターン
各要素の連携方法
| 対象 | 担当 | スキル |
|---|---|---|
| 構造化アプリケーションログ | Serilog(AddSerilog()) | serilog |
| リクエストのサマリーログ | UseSerilogRequestLogging() | serilog |
| トレース+メトリクス+OTLP エクスポート | OpenTelemetry SDK | opentelemetry |
| ヘルスチェックエンドポイント、相関 ID、ログレベルの方針 | このスキル | logging |
まずログ記録を設定します(残りのデバッグにはログが必要です)。次にヘルスチェック、最後にトレーシングを設定します。
相関 ID
// Middleware to set correlation ID
public class CorrelationIdMiddleware(RequestDelegate next)
{
private const string CorrelationIdHeader = "X-Correlation-Id";
public async Task InvokeAsync(HttpContext context)
{
var correlationId = context.Request.Headers[CorrelationIdHeader].FirstOrDefault()
?? Guid.NewGuid().ToString();
context.Items["CorrelationId"] = correlationId;
context.Response.Headers[CorrelationIdHeader] = correlationId;
using (LogContext.PushProperty("CorrelationId", correlationId))
{
await next(context);
}
}
}
// Program.cs — register early so every downstream log carries the ID
app.UseMiddleware<CorrelationIdMiddleware>();
ミドルウェアを使う理由:パイプラインの入口で一度プロパティを追加すれば、リクエストスコープ内のすべてのログイベントに付与されるため、呼び出し箇所ごとの設定は不要です。送信する HttpClient 呼び出しには、同じヘッダーを DelegatingHandler 経由で伝搬します(httpclient-factory スキルを参照)。
ヘルスチェック
// Program.cs
builder.Services.AddHealthChecks()
.AddNpgSql(builder.Configuration.GetConnectionString("Default")!,
name: "database", tags: ["ready"])
.AddRedis(builder.Configuration.GetConnectionString("Redis")!,
name: "redis", tags: ["ready"])
.AddRabbitMQ(builder.Configuration.GetConnectionString("RabbitMq")!,
name: "rabbitmq", tags: ["ready"]);
// Map endpoints
app.MapHealthChecks("/health/live", new HealthCheckOptions
{
Predicate = _ => false // No dependency checks — just "am I running?"
});
app.MapHealthChecks("/health/ready", new HealthCheckOptions
{
Predicate = check => check.Tags.Contains("ready")
});
エンドポイントを 2 つ用意する理由:ライブネスの失敗は「再起動する」、レディネスの失敗は「トラフィックの送信を止める」という意味です。これらを混同すると、データベースの遅延によってアプリがループ状態で再起動します。
ログレベルの方針
| レベル | 用途 | 環境ごとの既定値 |
|---|---|---|
| Debug | 診断の詳細、ペイロードのダンプ(本番環境では PII を含めない) | Development のみ |
| Information | 注文の確定、ジョブの完了などのビジネスイベント | Dev と staging |
| Warning | 再試行の実行、フォールバックの使用など、回復可能な異常 | すべての環境 — 本番環境の既定値 |
| Error | 対応が必要な失敗した操作 | すべての環境 |
| Fatal/Critical | アプリの継続が不可能 | すべての環境 |
本番環境の既定値を Warning にする理由:規模が大きくなると、Information レベルのリクエストノイズはログストレージの費用を押し上げ、重要なシグナルを埋もれさせます。名前空間のオーバーライドを使って、本当に必要なビジネスイベントには Information を維持します(serilog スキルの MinimumLevel.Override パターンを参照)。
アンチパターン
機密データをログに記録しない
// BAD — logging credentials
logger.LogInformation("User logged in: {Email} with password {Password}", email, password);
// GOOD — log identifiers, never secrets or PII at Information level
logger.LogInformation("User {UserId} logged in", userId);
ヘルスチェックのタグを省略しない
// BAD — all checks run for liveness AND readiness
app.MapHealthChecks("/health");
// GOOD — separate liveness (am I running?) from readiness (can I serve traffic?)
app.MapHealthChecks("/health/live", new() { Predicate = _ => false });
app.MapHealthChecks("/health/ready", new() { Predicate = c => c.Tags.Contains("ready") });
担当スキルが提供する機能を再実装しない
// BAD — hand-rolling Serilog bootstrap here from memory
builder.Host.UseSerilog(...); // legacy API — the serilog skill forbids this
// GOOD — load the serilog skill and use its two-stage AddSerilog() bootstrap
builder.Services.AddSerilog((services, lc) => lc.ReadFrom.Configuration(builder.Configuration)...);
判断ガイド
| シナリオ | 推奨事項 |
|---|---|
| アプリケーションのログ設定 | serilog を読み込む — AddSerilog() による二段階ブートストラップ |
| 分散トレーシング/メトリクス | opentelemetry を読み込む — OTLP エクスポーター |
| カスタムビジネスメトリクス | IMeterFactory+カウンター/ヒストグラム(opentelemetry スキル) |
| リクエストトレーシング | 相関 ID ミドルウェア(このスキル) |
| コンテナーのヘルスチェック | /health/live と /health/ready エンドポイント(このスキル) |
| ログストレージ | Seq(開発環境)、Elastic/Grafana/OTLP バックエンド(本番環境) |
| ログレベル | 開発環境では Debug、staging では Information、本番環境では Warning を既定値にする |
コメント
Markdown 対応