mcpField
G
go-observability

go-observability

github.com/eduardo-sl開発83157 回インストール2 週間前に更新
リポジトリ

go-observability について

go-observabilityは、本番Goサービスを計測する開発者向けのスキルです。構造化ログ、OpenTelemetryのトレースとメトリクス、アラート、シグナル間の関連付け、テレメトリーの終了処理を解説します。

主な機能

  1. 01slogによる構造化ログ
  2. 02OpenTelemetryのトレースパターン
  3. 03メトリクスとカーディナリティの指針
  4. 04アラートとSLOの実践
  5. 05ログ、トレース、メトリクスの関連付け
  6. 06テレメトリーのグレースフル終了

活用シーン

  1. 01Goサービスへのオブザーバビリティ導入
  2. 02ログとリクエストトレースの関連付け
  3. 03サービスのメトリクスとアラートの設計

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/eduardo-sl/go-agent-skills --skill go-observability

インストール後、Claude Code で/go-observabilityを入力して読み込まれたか確認します。

同梱ファイル

SKILL.md と一緒にインストールされる参考資料やスクリプト。

references/
alerting.md
metrics.md
slog.md
tracing.md

詳細

Go のオブザーバビリティ

オブザーバビリティは、本番サービスにとって必須です。すべてのサービスで構造化ログを出力し、メトリクスを公開し、トレースコンテキストを伝搬させる必要があります。ログには標準ライブラリの log/slog を、トレースとメトリクスには OpenTelemetry を使用してください。

コミュニティのデフォルト設定です。go-observability を明示的に置き換えるプロジェクトのスキルまたはエージェント設定ルールがある場合は、そちらが優先されます。

必要に応じて読み込む詳細なリファレンス資料:

  • references/slog.md — ハンドラーの設定、ロガーの注入、子ロガー、LogValuer による秘匿化、トレース ID ハンドラー、ホットパス、実行時のレベル変更。
  • references/tracing.md — トレーサープロバイダー、スパン、伝搬、サンプリング、プロファイルラベル、シャットダウン。
  • references/metrics.md — メトリクスの定義、HTTP 計測。
  • references/alerting.md — RED/USE、ヒストグラムとサマリーの比較、バーンレートアラート、アラートの衛生管理、エグザンプラー。

以下のセクションだけでは不十分な場合に限り、リファレンスファイルを読んでください。

1. slog による構造化ログ

本番環境では、JSON ハンドラーとともに log/slog(Go 1.21 以降)を使用してください。各行はキーと値のペアで構成し、書式付きの文章は使用しません。

// ✅ Good — structured, leveled
logger.Info("user created", slog.String("user_id", user.ID), slog.Duration("latency", elapsed))

// ❌ Bad — unparseable in production
log.Printf("user %s created in %v", user.ID, elapsed)

ロガーは依存関係として注入し、パッケージレベルのグローバルには決してアクセスしないでください。logger.With(...) で子ロガーを作成し、コンポーネント、メソッド、リクエスト ID を各呼び出し箇所で付与するのではなく、一度だけ付与してください。詳しくは references/slog.md を参照してください。

ログレベル — 一貫して使用する
レベル用途
Debug詳細な診断情報。本番環境では無効にします
Info通常の操作:リクエスト受信、ジョブ完了
Warn回復可能な問題:再試行の成功、非推奨の使用
Error対応が必要な障害:DB の停止、外部呼び出しの失敗

想定される状態(ユーザーが見つからない場合など)を Error レベルでログに記録してはいけません。→ Info または Warn を使用します。

機密データ — 絶対にログに記録しない
  • パスワード、トークン、API キー
  • クレジットカード番号全体、社会保障番号(SSN)
  • 個人を特定できる個人識別情報:メールアドレス、電話番号、IP アドレス
  • 個人情報(PII)を含む未加工のリクエスト本文
// ✅ Good — an opaque internal ID, no personal data
logger.Info("auth attempt", slog.String("user_id", userID), slog.Bool("success", ok))

// ❌ Bad — leaks a credential and a personal identifier
logger.Info("auth attempt", slog.String("password", password))
logger.Info("auth attempt", slog.String("user", email))

安定した内部 ID をログに記録し、個人データを保持することが許可されたシステム内で関連付けてください。ログの保持期間、検索アクセス、サードパーティのログ送信先が、ユーザーテーブルに適用される基準を満たすことはほとんどありません。

型の特性として秘匿化を実装してください。シークレットや個人データを保持する構造体に slog.LogValuer を実装し、どこで slog.Any(user) を呼び出しても、安全なフィールドのみがログに記録されるようにします。

コストと制御
  • ホットパスでは logger.LogAttrs を使用し、コストの高い属性は logger.Enabled でガードします。
  • HandlerOptions の slog.LevelVar を使うと、デプロイせずに実行時にレベルを変更できます。
  • slog.NewMultiHandler(Go 1.26 以降)を使うと、複数のハンドラーに出力を振り分けられます。

2. OpenTelemetry による分散トレーシング

ウォーターフォールで確認する価値のあるすべての操作(受信リクエスト、DB 呼び出し、送信 HTTP、重要なビジネス処理)でスパンを開始し、必ず defer span.End() を呼び出してください。

リファレンスの例では、次のルールに従っています。

  • スパン名は操作名(GetUser、db.query)にし、完全修飾関数名や doStuff にはしないでください。
  • 失敗時には span.RecordError(err) と span.SetStatus(codes.Error, ...) を呼び出してください。そうしないと、失敗したリクエストのスパンが緑色で表示されます。
  • ctx を呼び出しチェーン全体に渡してください。チェーンの途中で context.Background() を使うと、気付かないうちに新しいトレースが開始され、親とのリンクが切れます。
  • ParentBased(TraceIDRatioBased(r)) でサンプリングし、サービス間で同じトレースになるようにしてください。テールサンプリングはコレクターで行い、エラーと処理の遅いリクエストを保持します。
  • プロファイルは第 4 のシグナルです。本番環境では継続的プロファイリングを行い、上限を設けた pprof.Labels(ルート、テナント層)を使って、負荷の高い関数を分類します。

設定と例は references/tracing.md を参照してください。

3. OpenTelemetry/Prometheus によるメトリクス

種類用途例
Counter単調に増加する値リクエスト総数、エラー総数
Gauge増減する値アクティブな接続数、キューの深さ
Histogram値の分布リクエストのレイテンシ、レスポンスサイズ

命名規則は <namespace>_<subsystem>_<name>_<unit> です。

http_request_duration_seconds     ✅ (unit in name)
http_requests_total               ✅ (counter with _total suffix)
db_connections_active             ✅ (gauge, no suffix needed)
user_signups                      ❌ (missing _total for counter)
requestLatency                    ❌ (camelCase, no unit)

カーディナリティは上限を設けてください。ラベルにはルートパターン、メソッド、ステータスを使い、ユーザー ID、リクエスト ID、生の r.URL.Path は使わないでください。これらは値ごとに新しい時系列を生成し、最終的にメトリクスバックエンドを停止させます。

定義とミドルウェアについては references/metrics.md を参照してください。

4. ログ、トレース、メトリクスの連携

トレース ID のないログ行は、それを生成したリクエストと関連付けられません。ハンドラーを一度ラップして、すべての *Context ログ呼び出しで ctx から trace_id と span_id を追加します。ラッパーでは WithAttrs と WithGroup もオーバーライドする必要があります。そうしないと、子ロガーでこれらの情報が失われます。レイテンシのヒストグラムにエグザンプラー(トレース ID)を付与すると、p99 のスパイクから問題のあるトレースへ移動できます。

5. アラートと SLO

  • サービスではルートごとに RED(レート、エラー、継続時間)を、プールとキューでは USE(使用率、飽和度、エラー)を測定します。
  • レイテンシは必ず Histogram を使い、Summary は使わないでください。Summary の分位点はインスタンス間で集計できません。SLO のしきい値を囲むようにバケットを設定します。
  • 長いウィンドウと短いウィンドウの両方で SLO の バーンレート を監視してページングし、遅いバーンはチケットを作成します。
  • 原因ではなく症状に対してアラートを設定します。irate() ではなく rate() を使い、存在すべき時系列には absent() を使います。すべてのページングアラートにランブックへのリンクを付けます。

クエリとエグザンプラーの設定については references/alerting.md を参照してください。

6. テレメトリのグレースフルシャットダウン

バッチスパンプロセッサはスパンをメモリに保持します。終了時に専用のタイムアウトを設定して tp.Shutdown(ctx) を呼び出してください。そうしないと、クラッシュ前の最後の数秒間、つまり重要なデータがコレクターに届きません。

references/tracing.md にある両方の例を参照してください。

検証チェックリスト

  1. すべてのログ出力で fmt.Printf や log.Printf ではなく、構造化されたキーと値のペアを使う log/slog を使用している
  2. ロガーはグローバルとして使わず、依存関係として注入している
  3. ログ出力に機密データ(パスワード、トークン、PII)が含まれていない
  4. すべての関数呼び出しを通じて、context.Context によってトレースコンテキストを伝播している
  5. 重要な処理(DB 呼び出し、HTTP リクエスト、ビジネスロジック)に対してスパンを作成している
  6. span.RecordError(err) でスパンにエラーを記録し、エラーステータスを設定している
  7. メトリクスが命名規則(_seconds、_total、_bytes)に従っている
  8. メトリクスにカーディナリティの高いラベル(ユーザー ID、リクエスト ID)が含まれていない
  9. サービス終了時にテレメトリプロバイダーをグレースフルにシャットダウンしている
  10. 関連付けのため、ログエントリにトレース ID を含めている
  11. レイテンシをヒストグラムとして記録し、ページングアラートをランブック付きの SLO バーンレートアラートにしている

コメント