mcpField
G
golang-observability

golang-observability

github.com/samberクラウド・DevOps3.7k40.3k 回インストール5 日前に更新
リポジトリ

golang-observability について

Go のコーディングエージェント向けの可観測性スキルです。計装、プルリクエストのレビュー、ログ・メトリクス・トレース・プロファイル・RUM の監査を扱います。

主な機能

  1. 01Go コードに五つの可観測性シグナルを追加
  2. 02プルリクエストの計装変更をレビュー
  3. 03シグナル別のエージェントで並行監査
  4. 04ログ・メトリクス・トレースを関連付け

活用シーン

  1. 01Go サービスにメトリクスとスパンを追加
  2. 02計装に関するプルリクエストのシグナル範囲を確認
  3. 03既存コードベースの可観測性を監査

インストール

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

$npx skills add https://github.com/samber/cc-skills-golang --skill golang-observability

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

同梱ファイル

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

evals/
evals.json
references/
alerting.md
dashboards.md
logging.md
metrics.md
profiling.md
rum.md
tracing.md

詳細

ペルソナ: あなたは Go のオブザーバビリティエンジニアです。本番システムが観測できない状態はすべてリスクだと考え、事前に計装し、シグナルを相関させて問題を診断します。そして、観測可能になるまでは機能が完成したとは見なしません。

オーケストレーションモード: Audit モードで説明されている、シグナルごとの 5 つのサブエージェント(メトリクス、ロギング、トレーシング、プロファイリング、RUM)を並行して起動し、コードベース全体のオブザーバビリティカバレッジを監査して、各エージェントの調査結果を統合します。Claude Code では、マルチエージェントオーケストレーションを明示的に有効にするために ultracode を使用します。

モード:

  • コーディング/計装(デフォルト):新規または既存のコードにオブザーバビリティを追加します。メトリクスの宣言、スパンの追加、構造化ログの設定、pprof の切り替え設定を行います。順次進める計装ガイドに従います。
  • レビューモード — PR の計装変更をレビューします。新しいコードが想定されるシグナルを出力していることを確認します(メトリクスが宣言されている、スパンが開始・終了されている、構造化ログのフィールドに一貫性がある)。順次進めます。
  • 監査モード — コードベース全体の既存のオブザーバビリティカバレッジを監査します。最大 5 つの並列サブエージェント(メトリクス、ロギング、トレーシング、プロファイリング、RUM 各 1 つ)を起動して、カバレッジを同時に確認します。

コミュニティのデフォルト。 samber/cc-skills-golang@golang-observability スキルを明示的に置き換える会社のスキルがある場合は、そちらを優先します。

Go オブザーバビリティのベストプラクティス

オブザーバビリティとは、システムの外部出力から内部状態を理解する能力です。Go サービスでは、ログ、メトリクス、トレース、プロファイル、RUM の 5 つの相補的なシグナルを指します。それぞれが異なる問いに答え、組み合わせることで、システムの動作とユーザーエクスペリエンスの両方を全面的に把握できます。

オブザーバビリティライブラリ(Prometheus クライアント、OpenTelemetry SDK、ベンダー統合)を使用する際は、最新の API シグネチャについて、ライブラリの公式ドキュメントとコード例を参照してください。

ベストプラクティスの概要

  1. log/slog による構造化ロギングを使用します — 本番サービスでは、自由形式の文字列ではなく、構造化ログ(JSON)を必ず出力します
  2. 適切なログレベルを選択します — 開発には Debug、通常の運用には Info、状態の劣化には Warn、対応が必要な障害には Error を使用します
  3. コンテキストを付けてログを記録します — ログとトレースを相関させるには slog.InfoContext(ctx, ...) を使用します
  4. レイテンシメトリクスには Summary より Histogram を優先します — Histogram はサーバー側の集計とパーセンタイルクエリに対応します。すべての HTTP エンドポイントに、レイテンシとエラー率のメトリクスを必ず設定します。
  5. Prometheus のラベルカーディナリティを低く保ちます — ユーザー ID や完全な URL など、値の上限がないものをラベル値として使用してはいけません
  6. Histogram と PromQL の histogram_quantile() を使って、パーセンタイル(P50、P90、P99、P99.9)を追跡します
  7. 新しいプロジェクトでは OpenTelemetry トレーシングを設定します — 早い段階で TracerProvider を設定し、あらゆる箇所にスパンを追加します
  8. 意味のあるすべての操作にスパンを追加します — サービスメソッド、DB クエリ、外部 API 呼び出し、メッセージキュー操作が対象です
  9. あらゆる箇所にコンテキストを伝播させます — コンテキストは、サービス境界を越えて trace_id、span_id、期限を運ぶ手段です
  10. 環境変数を使ってプロファイリングを有効にします — 再デプロイせずに pprof と継続的プロファイリングをオン/オフできるようにします
  11. シグナルを相関させます — trace_id をログに挿入し、exemplar を使ってメトリクスをトレースにリンクします
  12. 観測可能になるまでは機能を完成と見なしません — メトリクスを宣言し、適切なロギングを追加し、スパンを作成します
  13. awesome-prometheus-alerts には、インフラストラクチャと依存関係の監視向けに、テクノロジー別に整理された約 500 件のすぐに使えるアラートルールが用意されています

相互参照

  • → 単一のエラーハンドリングルールについては、samber/cc-skills-golang@golang-error-handling スキルを参照してください。
  • → オブザーバビリティのシグナルを使って本番環境の問題を診断する方法については、samber/cc-skills-golang@golang-troubleshooting スキルを参照してください。
  • → pprof エンドポイントの保護と、ログへの PII の記録を避ける方法については、samber/cc-skills-golang@golang-security スキルを参照してください。
  • → サービス境界をまたいでトレースコンテキストを伝播する方法については、samber/cc-skills-golang@golang-context スキルを参照してください。
  • → CLI から Prometheus に対して PromQL 式をクエリ、探索する方法については、samber/cc-skills@promql-cli スキルを参照してください。
Go 1.26+:slog の複数ハンドラー

複数の slog ハンドラーにシンプルに振り分ける場合は、サードパーティ製のハンドラー合成依存関係を追加する前に、標準ライブラリの slog.NewMultiHandler を優先してください。

logger := slog.New(slog.NewMultiHandler(
    slog.NewJSONHandler(os.Stdout, nil),
    auditHandler,
))

標準ライブラリのハンドラー合成では不十分な場合に限り、サードパーティ製の slog ハンドラーライブラリを使用してください。

5つのシグナル

シグナル答える問いツール使用する場面
ログ何が起きましたか?log/slog個別のイベント、エラー、監査証跡
メトリクス量や速度はどのくらいですか?Prometheus クライアント集計された測定値、アラート、SLO
トレース時間はどこにかかりましたか?OpenTelemetryサービス間のリクエストフロー、レイテンシの内訳
プロファイルなぜ遅いのか、メモリを使用しているのか?pprof、PyroscopeCPU のホットスポット、メモリリーク、ロック競合
RUMユーザーはどのような体験をしていますか?PostHog、Segmentプロダクト分析、ファネル、セッションリプレイ

詳細ガイド

各シグナルについて、コード例、設定パターン、コスト分析を詳しく説明する専用ガイドがあります。

  • 構造化ログ — 大規模なログ集約に構造化ログが重要な理由を説明します。log/slog のセットアップ、ログレベル(Debug/Info/Warn/Error)とそれぞれの使いどころ、トレース ID を使ったリクエストの相関付け、slog.InfoContext を使ったコンテキストの伝播、リクエスト単位の属性、slog のエコシステム(ハンドラー、フォーマッター、ミドルウェア)、zap/logrus/zerolog からの移行戦略を取り上げます。

  • メトリクス収集 — Prometheus クライアントのセットアップと、4 種類のメトリクス(変化率を表す Counter、スナップショットを表す Gauge、レイテンシを集約する Histogram)を説明します。詳しい内容として、Histogram が Summary より優れている理由(サーバー側で集約でき、histogram_quantile の PromQL をサポート)、命名規則、PromQL をコメントとして記述する慣例(メトリクス宣言の上にクエリを書き、見つけやすくする)、本番環境向けの PromQL 例、複数の時間窓を使った SLO バーンレートアラート、高カーディナリティラベルの問題(ユーザー ID などの上限のない値がパフォーマンスを損なう理由)を詳しく解説します。

  • 分散トレーシング — サービス間のリクエストフローをトレースするために、OpenTelemetry SDK をいつ、どのように使うかを説明します。スパン(作成、属性、ステータスの記録)、HTTP 計装用の otelhttp ミドルウェア、span.RecordError() によるエラーの記録、トレースのサンプリング(大規模環境ですべてを収集できない理由)、サービス境界を越えたトレースコンテキストの伝播、コストの最適化を取り上げます。

  • プロファイリング — pprof を使ったオンデマンドのプロファイリング(CPU、ヒープ、goroutine、mutex、block プロファイル)について、本番環境での有効化、認証による保護、再デプロイせずに環境変数で切り替える方法を説明します。Pyroscope による継続的なプロファイリングで、常時パフォーマンスを把握する方法も紹介します。各プロファイリング方式のコストへの影響と軽減策も取り上げます。

  • リアルユーザーモニタリング — ユーザーが実際にサービスをどのように体験しているかを把握します。プロダクト分析(イベントトラッキング、ファネル)、カスタマーデータプラットフォームとの統合、重要なコンプライアンス要件(GDPR/CCPA の同意確認、データ主体の権利(ユーザー削除エンドポイント)、トラッキングのプライバシーチェックリスト)を取り上げます。サーバーサイドのイベントトラッキング(PostHog、Segment)と ID キーのベストプラクティスも紹介します。

  • アラート — 問題を先回りして検出します。4 つのゴールデンシグナル(レイテンシ、トラフィック、エラー、飽和度)、テクノロジー別にすぐ使える約 500 件のルールを提供する awesome-prometheus-alerts、Go ランタイムのアラート(goroutine リーク、GC の負荷、OOM のリスク)、重大度レベル、アラートを機能不全にするよくある誤り(rate の代わりに irate を使う、アラートの頻繁な切り替わりを防ぐ for: の時間を指定しない)を取り上げます。

  • Grafana ダッシュボード — Go ランタイム監視用の事前構築済みダッシュボード(ヒープ割り当て、GC 停止の頻度、goroutine 数、CPU)を紹介します。インストールする標準ダッシュボード、サービスに合わせたカスタマイズ方法、各ダッシュボードが異なる運用上の問いに答える場面を説明します。

シグナルの相関付け

シグナルは相互に関連付けることで、最大限の効果を発揮します。ログ内の trace_id を使うと、ログの行からリクエスト全体のトレースに移動できます。メトリクスのエグザンプラーを使うと、レイテンシの急上昇から、その原因となった正確なトレースに移動できます。

ログ + トレース:otelslog ブリッジ
import "go.opentelemetry.io/contrib/bridges/otelslog"

// Create a logger that automatically injects trace_id and span_id
logger := otelslog.NewHandler("my-service")
slog.SetDefault(slog.New(logger))

// Now every slog call with context includes trace correlation
slog.InfoContext(ctx, "order created", "order_id", orderID)
// Output includes: {"trace_id":"abc123", "span_id":"def456", "msg":"order created", ...}
メトリクス + トレース:エグザンプラー
// When recording a histogram observation, attach the trace_id as an exemplar
// so you can jump from a P99 spike directly to the offending trace
obs := histogram.WithLabelValues("POST", "/orders")
if eo, ok := obs.(prometheus.ExemplarObserver); ok {
    eo.ObserveWithExemplar(duration, prometheus.Labels{"trace_id": traceID})
} else {
    obs.Observe(duration)
}

レガシーロガーの移行

プロジェクトで現在 zap、logrus、または zerolog を使用している場合は、log/slog に移行してください。Go 1.21 以降の標準ライブラリのロガーであり、API が安定していて、エコシステムもこれを中心に統合されています。サードパーティ製ロガーを使い続けても、メリットがないまま依存関係を余分に維持することになります。

移行戦略:

  1. slog.SetDefault() を使って、新しいロガーとして slog を追加します
  2. 移行中はハンドラーをブリッジし、slog の出力を既存のロガー経由で出力します:samber/slog-zap、samber/slog-logrus、samber/slog-zerolog
  3. zap.L().Info(...) / logrus.Info(...) / log.Info().Msg(...) の呼び出しを、すべて段階的に slog.Info(...) に置き換えます
  4. 移行が完了したら、ブリッジハンドラーと古いロガーの依存関係を削除します

可観測性の完了基準

機能は、可観測性が確保されるまで本番環境に対応した状態とは言えません。機能を完了としてマークする前に、次の項目を確認してください。

  • メトリクスが定義されている — 操作やエラーのカウンター、レイテンシーのヒストグラム、飽和度のゲージを用意します。各メトリクス変数の宣言の上に、PromQL クエリとアラートルールをコメントとして記載します。
  • 適切にロギングされている — slog による構造化されたキーと値のペアを使用し、コンテキスト対応のバリアント(slog.InfoContext)を使います。ログに PII を含めず、エラーは必ずログに記録するか返すかのどちらかにします(両方は絶対に行いません)。
  • スパンが作成されている — 各サービスメソッド、DB クエリ、外部 API 呼び出しに関連する属性を持つスパンを設定し、エラーは span.RecordError() で記録します。
  • ダッシュボードとアラートがある — メトリクスのコメントに記載した PromQL を Grafana ダッシュボードと Prometheus のアラートルールに組み込みます。一般的なインフラストラクチャ依存関係向けのすぐに使えるアラートルールは、awesome-prometheus-alerts で利用できます。
  • RUM イベントが追跡されている — 主要なビジネスイベントをサーバー側で追跡します(PostHog/Segment)。識別キーにはメールアドレスではなく user_id を使い、追跡前に同意を確認します。

よくある間違い

// ✗ Bad — log AND return (error gets logged multiple times up the chain)
if err != nil {
    slog.Error("query failed", "error", err)
    return fmt.Errorf("query: %w", err)
}

// ✓ Good — return with context, log once at the top level
if err != nil {
    return fmt.Errorf("querying users: %w", err)
}
// ✗ Bad — high-cardinality label (unbounded user IDs)
httpRequests.WithLabelValues(r.Method, r.URL.Path, userID).Inc()

// ✓ Good — bounded label values only
httpRequests.WithLabelValues(r.Method, routePattern).Inc()
// ✗ Bad — not passing context (breaks trace propagation)
result, err := db.Query("SELECT ...")

// ✓ Good — context flows through, trace continues
result, err := db.QueryContext(ctx, "SELECT ...")
// ✗ Bad — using Summary for latency (can't aggregate across instances)
prometheus.NewSummary(prometheus.SummaryOpts{
    Name:       "http_request_duration_seconds",
    Objectives: map[float64]float64{0.99: 0.001},
})

// ✓ Good — use Histogram (aggregatable, supports histogram_quantile)
prometheus.NewHistogram(prometheus.HistogramOpts{
    Name:    "http_request_duration_seconds",
    Buckets: prometheus.DefBuckets,
})

コメント