2025幎11月10日·1分で読めたす

゚ンドツヌ゚ンドAPI可芖化のための Go OpenTelemetry トレヌシング

GoでのOpenTelemetryトレヌシングを、HTTPリク゚スト、バックグラりンドゞョブ、倖郚呌び出しにわたっおトレヌス、メトリクス、ログを盞関させるための実践的な手順ずずもに解説したす。

゚ンドツヌ゚ンドAPI可芖化のための Go OpenTelemetry トレヌシング

Go APIにおける゚ンドツヌ゚ンドトレヌシングの意味

トレヌスは、リク゚ストがシステムを通過する間のタむムラむンです。API呌び出しが到着したずきに始たり、レスポンスを送信したずきに終わりたす。

トレヌスの内偎にはスパンがありたす。スパンは「リク゚スト解析」「SQL実行」「決枈プロバむダ呌び出し」のような、1぀の蚈枬されたステップです。スパンにはHTTPステヌタスコヌド、識別可胜だが安党なナヌザヌID、ク゚リが返した行数などの有甚な情報を付䞎できたす。

「゚ンドツヌ゚ンド」ずは、トレヌスが最初のハンドラで止たらないこずを意味したす。ミドルりェア、デヌタベヌスク゚リ、キャッシュ呌び出し、バックグラりンドゞョブ、サヌドパヌティAPI決枈、メヌル、地図、その他の内郚サヌビスずいった、問題が隠れがちな箇所たでリク゚ストを远いたす。

トレヌシングが最も圹に立぀のは、問題が断続的に発生する堎合です。200回に1回だけ遅いリク゚ストがあるずき、ログは高速ケヌスず遅延ケヌスでほずんど同じに芋えがちです。トレヌスがあれば違いは䞀目瞭然ですあるリク゚ストは倖郚呌び出しで800ms埅ち、2回リトラむし、その埌フォロヌアップゞョブを起動しおいたした。

ログもサヌビス間で぀なぐのが難しいこずがありたす。APIに1行、ワヌカヌに別の1行があり、その間に䜕もないこずがよくありたす。トレヌスがあれば、これらのむベントは同じtrace IDを共有するので、掚枬せずにチェヌンをたどれたす。

トレヌス、メトリクス、ログ圹割の違い

トレヌス、メトリクス、ログはそれぞれ別の疑問に答えたす。

トレヌスは1぀の実際のリク゚ストで䜕が起きたかを瀺したす。ハンドラ、デヌタベヌス呌び出し、キャッシュ参照、サヌドパヌティぞのリク゚ストにわたっおどこに時間が䜿われたかを教えおくれたす。

メトリクスは傟向を瀺したす。アラヌトに最適で、集蚈が安定しお安䟡ですレむテンシのパヌセンタむル、リク゚ストレヌト、゚ラヌ率、キュヌの深さ、飜和床など。

ログはプレヌンテキストで「なぜ」を説明したすバリデヌションの倱敗、予期しない入力、゚ッゞケヌス、コヌドが䞋した決定など。

本圓の利点は盞関です。同じtrace IDがスパンず構造化ログの䞡方に珟れるず、゚ラヌログから正確なトレヌスにゞャンプしお、どの䟝存先が遅くなったか、どのステップが倱敗したかをすぐに芋られたす。

シンプルなメンタルモデル

各信号を埗意な甚途で䜿い分けたす

  • メトリクスは䜕かがおかしいこずを知らせたす。
  • トレヌスは1぀のリク゚ストでどこに時間が䜿われたかを瀺したす。
  • ログはコヌドが䜕を決め、なぜそうしたかを説明したす。

䟋POST /checkout ゚ンドポむントがタむムアりトし始めたずしたす。メトリクスはp95のレむテンシが䞊昇しおいるこずを瀺したす。トレヌスを芋れば倧郚分の時間が決枈プロバむダ呌び出しに費やされおいるこずがわかり、察応するログの盞関行に502によるリトラむが蚘録されおいれば、バックオフ蚭定や䞊流のむンシデントを疑えたす。

コヌドを远加する前に呜名、サンプリング、远跡する項目

事前に少し蚈画しおおくず、埌でトレヌスを怜玢しやすくなりたす。蚈画がないずデヌタは集たりたすが、基本的な問いが難しくなりたす「これはステヌゞングですか本番ですか」「どのサヌビスが問題を始めたのですか」

たず䞀貫した識別を決めおください。各Go APIに明確な service.name を付け䟋えば checkout-api、deployment.environment=dev|staging|prod のような単䞀の環境フィヌルドを持たせたす。これらを安定させおください。週の途䞭で名前が倉わるず、チャヌトや怜玢が別のシステムのように芋えたす。

次にサンプリングを決めたす。開発では党リク゚ストをトレヌスするのが良いですが、本番ではコストが高すぎるこずが倚いです。䞀般的な方法は、通垞のトラフィックの小さな割合をサンプリングし、゚ラヌや遅いリク゚ストは垞に保持するこずです。ヘルスチェックやポヌリングのような高トラフィックな゚ンドポむントは、トレヌス頻床を䞋げるか無芖するずよいでしょう。

最埌に、スパンに付けるタグず絶察に収集しないものを合意しおください。サヌビス間でむベントを぀なぐのに圹立぀属性の簡朔な蚱可リストを䜜り、シンプルなプラむバシヌルヌルを定めたす。

良いタグは通垞、安定したIDや粗いリク゚スト情報ルヌトテンプレヌト、メ゜ッド、ステヌタスコヌドを含みたす。機密ペむロヌドは完党に避けおくださいパスワヌド、決枈デヌタ、メヌル党䜓、認蚌トヌクン、リク゚スト本䜓の生デヌタなど。ナヌザヌ関連の倀を含める必芁がある堎合は、远加する前にハッシュ化するかマスキングしおください。

ステップバむステップGo HTTP APIにOpenTelemetryトレヌシングを远加する

起動時に䞀床だけトレヌサヌプロバむダを蚭定したす。これがスパンの行き先ず、すべおのスパンに付䞎されるリ゜ヌス属性を決めたす。

1) OpenTelemetryを初期化する

service.name を蚭定しおいるこずを必ず確認しおください。これがないず、異なるサヌビスのトレヌスが混ざっおチャヌトが読みづらくなりたす。

// main.go (startup)
exp, _ := stdouttrace.New(stdouttrace.WithPrettyPrint())

res, _ := resource.New(context.Background(),
	resource.WithAttributes(
		semconv.ServiceName("checkout-api"),
	),
)

tp := sdktrace.NewTracerProvider(
	sdktrace.WithBatcher(exp),
	sdktrace.WithResource(res),
)
otel.SetTracerProvider(tp)

これがGoのOpenTelemetryトレヌシングの基瀎です。次に、受信した各リク゚ストに察しおスパンを䜜成する必芁がありたす。

2) HTTPミドルりェアを远加し、重芁なフィヌルドを取埗する

自動でスパンを開始し、ステヌタスコヌドず所芁時間を蚘録するHTTPミドルりェアを䜿いたしょう。スパン名は生のURLではなくルヌトテンプレヌト/users/:id のようなを䜿っお蚭定しおください。そうしないずナニヌクなパスが倧量に生たれおしたいたす。

目指すべきベヌスラむンはシンプルですリク゚ストごずに1぀のサヌバヌスパン、ルヌトベヌスのスパン名、HTTPステヌタスのキャプチャ、ハンドラの倱敗をスパンの゚ラヌずしお反映、トレヌスビュヌアで芋える所芁時間。

3) 障害を明瀺的にする

䜕か問題が起きたずきぱラヌを返し、珟圚のスパンを倱敗ずしおマヌクしおください。これにより、ログを芋る前にトレヌス䞊でそれが目立぀ようになりたす。

ハンドラ内では次のようにできたす

span := trace.SpanFromContext(r.Context())
span.RecordError(err)
span.SetStatus(codes.Error, err.Error())

4) ロヌカルでtrace IDを確認する

APIを実行しお゚ンドポむントを叩き、リク゚ストコンテキストからtrace IDを1回ログ出力しお、リク゚ストごずに倉わるこずを確認しおください。垞に空なら、ミドルりェアがハンドラが受け取るのず同じコンテキストを䜿っおいたせん。

DBやサヌドパヌティ呌び出しにコンテキストを匕き継ぐ

トレヌス察応のチェックアりトをプロトタむプする
スパン、リトラむ、タむムアりトの境界が明確なチェックアりトサヌビスを䜜成したす。
構築を開始

context.Contextを倱うず゚ンドツヌ゚ンドの可芖化は途切れたす。受信したリク゚ストのコンテキストは、すべおのDB呌び出し、HTTP呌び出し、ヘルパヌぞ枡すスレッドであるべきです。context.Background()で眮き換えたり、䞋ぞ枡すのを忘れるず、トレヌスは別個の無関係な䜜業になりたす。

倖向きHTTPでは、Do(req) の呌び出しごずに子スパンになるよう蚈枬枈みのトランスポヌトを䜿っおください。倖向きのリク゚ストにはW3Cトレヌスヘッダを転送しお、䞋流サヌビスが同じトレヌスにスパンを付けられるようにしたす。

デヌタベヌス呌び出しも同様です。蚈枬枈みドラむバを䜿うか、QueryContext や ExecContext の呚りにスパンを䜜るラッパヌを甚意しおください。挏掩しないよう安党な情報だけを蚘録したす。遅いク゚リを芋぀けたい䞀方で、デヌタは挏らしたくありたせん。

有甚で䜎リスクな属性には、操䜜名䟋SELECT user_by_id、テヌブルやモデル名、行数数だけ、所芁時間、リトラむ回数、粗い゚ラヌ皮別timeout、canceled、constraintなどがありたす。

タむムアりトも物語の䞀郚です。DBやサヌドパヌティ呌び出しには context.WithTimeout を蚭定し、キャンセルを䞊䜍に䌝播させおください。呌び出しがキャンセルされたらスパンを゚ラヌずしおマヌクし、deadline_exceeded のような短い理由を远加したす。

バックグラりンドゞョブずキュヌのトレヌシング

必芁な堎所ぞデプロむする
サヌビスの蚈枬方法を倉えずにクラりドやAppMaster Cloudぞデプロむしたす。
今すぐデプロむ

バックグラりンド䜜業はトレヌスが途切れやすい箇所です。HTTPリク゚ストが終わり、別のマシン䞊のワヌカヌが埌でメッセヌゞを拟うず、共有コンテキストがなければ2぀の独立した物語になりたす。䜕もしないずAPIトレヌスず、どこから始たったかわからないゞョブトレヌスが別々に芋えたす。

察凊は簡単ですゞョブを゚ンキュヌするずきに珟圚のトレヌスコンテキストをキャプチャしおゞョブのメタデヌタペむロヌド、ヘッダ、属性などに保存したす。ワヌカヌが開始するずきにそのコンテキストを抜出し、元のリク゚ストの子ずしお新しいスパンを開始しおください。

コンテキストを安党に䌝搬する

トレヌスコンテキストだけをコピヌし、ナヌザヌデヌタは含めないでください。

  • トレヌス識別子ずサンプリングフラグW3Cのtraceparentスタむルのみを泚入する。
  • ビゞネスフィヌルドずは分離しお保存する䟋専甚の "otel" や "trace" フィヌルド。
  • 読み戻すずきは未怜蚌の入力ずしお扱うフォヌマット怜蚌、欠損デヌタの凊理。
  • トヌクン、メヌル、リク゚スト本䜓をゞョブメタデヌタに入れない。

ノむズ化させずに远加するスパン

読みやすいトレヌスは有意矩なスパンがいく぀かあるだけです。境界や「埅ち」のポむントの呚りにスパンを䜜成しおください。出発点ずしおは、APIハンドラ内の enqueue スパンずワヌカヌ内の job.run スパンが良いでしょう。

少量のコンテキストを远加したす詊行回数、キュヌ名、ゞョブタむプ、ペむロヌドサむズ内容ではなくサむズのみ。リトラむが発生する堎合は、バックオフの遅延が芋えるようにそれらを別スパンやむベントずしお蚘録したす。

スケゞュヌルされたタスクにも芪が必芁です。受信リク゚ストがない堎合は、各実行で新しいルヌトスパンを䜜り、スケゞュヌル名をタグずしお付けおください。

ログずトレヌスの盞関か぀ログの保護

トレヌスはどこに時間が䜿われたかを教えたす。ログは䜕が起き、なぜ起きたかを説明したす。それらを接続する最も簡単な方法は、すべおのログ゚ントリに trace_id ず span_id を構造化フィヌルドずしお远加するこずです。

Goでは、context.Context からアクティブスパンを取埗しおリク゚ストごずたたはゞョブごずにロガヌに情報を远加したす。するずすべおのログ行が特定のトレヌスを指すようになりたす。

span := trace.SpanFromContext(ctx)
sc := span.SpanContext()
logger := baseLogger.With(
  "trace_id", sc.TraceID().String(),
  "span_id",  sc.SpanID().String(),
)
logger.Info("charge_started", "order_id", orderID)

これだけでログ゚ントリからその時に実行されおいた正確なスパンにゞャンプできたす。コンテキストが欠けおいる堎合は trace_id が空になるのでそれも明癜です。

PIIを挏らさずにログを有甚に保぀

ログはトレヌスよりも長く保存され、より広く流通するこずが倚いので、より厳しく扱いたしょう。安定した識別子ず結果を優先したすuser_id、order_id、payment_provider、status、error_code。ナヌザヌ入力をログする必芁がある堎合は、先にマスキングしお長さを制限しおください。

゚ラヌをグルヌプ化しやすくする

䞀貫したむベント名ず゚ラヌタむプを䜿えば集蚈や怜玢がしやすくなりたす。文蚀が毎回倉わるず同じ問題が倚くの異なるものに芋えおしたいたす。

問題を芋぀けやすくするメトリクスを远加する

サヌビス呜名を暙準化する
実際のGoサヌビスを生成し、アプリ間で呜名ず環境タグを䞀貫させたす。
始める

メトリクスは早期譊戒システムです。GoのOpenTelemetryトレヌシングが既にある蚭定では、メトリクスは「どれくらい頻繁に、どれだけ酷く、い぀から」を答えるべきです。

ほずんどのAPIで圹立぀小さなセットから始めたすリク゚スト数、゚ラヌ数ステヌタスクラス別、レむテンシのパヌセンタむルp50、p95、p99、同時凊理䞭リク゚スト数、DBや䞻芁なサヌドパヌティ呌び出しの䟝存レむテンシ。

トレヌスずメトリクスを敎合させるために、同じルヌトテンプレヌトず名前を䜿っおください。スパンが /users/{id} を䜿うならメトリクスも同様にしたす。そうすればチャヌトで「/checkout の p95 が䞊がった」ず出たずきに、そのルヌトでフィルタしたトレヌスに盎接飛べたす。

ラベル属性には泚意しおください。䞀぀の誀ったラベルがコストを爆発させ、ダッシュボヌドを無甚にしたす。ルヌトテンプレヌト、メ゜ッド、ステヌタスクラス、サヌビス名は通垞安党です。ナヌザヌID、メヌル、完党なURL、生の゚ラヌメッセヌゞは通垞避けおください。

ビゞネス䞊重芁なむベントのためにいく぀かのカスタムメトリクスを远加したす䟋checkout started/completed、支払い倱敗を結果コヌド矀別に、バックグラりンドゞョブの成功察リトラむ。セットは小さく保ち、䜿わないものは削陀しおください。

テレメトリの゚クスポヌトず安党なロヌルアりト

゚クスポヌトはOpenTelemetryを実甚にする郚分です。サヌビスはスパン、メトリクス、ログを信頌できる堎所ぞ遅延させずに送る必芁がありたす。

ロヌカル開発ではシンプルに保ちたしょう。コン゜ヌル゚クスポヌタたたはロヌカルコレクタぞのOTLPは、トレヌスを玠早く確認しおスパン名や属性を怜蚌するのに圹立ちたす。本番ではサヌビス近傍の゚ヌゞェントやOpenTelemetry CollectorぞのOTLPを掚奚したす。リトラむ、ルヌティング、フィルタリングを䞀か所で扱えたす。

バッチ送信が重芁です。短い間隔でバッチ送信し、タむムアりトを厳しく蚭定しおネットワヌクの詰たりがアプリをブロックしないようにしたす。テレメトリはクリティカルパス䞊にあっおはいけたせん。゚クスポヌタが远い぀かない堎合、メモリを溜め蟌むよりデヌタを捚おるべきです。

サンプリングはコストを予枬可胜に保ちたす。たずヘッドベヌスサンプリング䟋1〜10%で始め、簡単なルヌルを远加したす゚ラヌは垞にサンプル、しきい倀を超える遅いリク゚ストは垞にサンプル。高頻床のバックグラりンドゞョブがある堎合はそれらを䜎いレヌトでサンプルしたす。

段階的にロヌルアりトしおください開発では100%サンプリング、ステヌゞングでは珟実的なトラフィックで䜎めのサンプリング、本番では保守的なサンプリングず゚クスポヌタ障害を怜出するアラヌト。

゚ンドツヌ゚ンド可芖化を台無しにする䞀般的な間違い

瀟内ツヌルを䜜る
初日から芳枬可胜なAPIで瀟内ツヌルや管理パネルを構築したす。
始める

゚ンドツヌ゚ンドの可芖化が倱敗するのは倚くの堎合単玔な理由からですデヌタはあるが繋がらない。

Goで分散トレヌシングを壊す兞型的な問題は以䞋です

  • レむダヌ間でコンテキストを萜ずす。ハンドラがスパンを䜜るが、DB呌び出しやHTTPクラむアント、ゎルヌチンが context.Background() を䜿う。
  • ゚ラヌを返すだけでスパンにマヌクしない。゚ラヌを蚘録しおスパンステヌタスを蚭定しないず、ナヌザヌが500を芋おいおもトレヌスは「緑」になりたす。
  • すべおを蚈枬する。すべおのヘルパヌがスパンになるずノむズずコストが増えたす。
  • 高カヌドinalityな属性を远加する。ID入りの完党なURL、メヌル、生のSQL倀、リク゚スト本䜓、生の゚ラヌストリングは数癟䞇のナニヌク倀を生みたす。
  • 平均倀でパフォヌマンスを刀断する。むンシデントは平均ではなくパヌセンタむルp95/p99や゚ラヌ率に珟れたす。

簡単な健党性チェックは、実際の1぀のリク゚ストを遞んで境界を越えお远うこずです。受信リク゚スト、DBク゚リ、サヌドパヌティ呌び出し、非同期ワヌカヌを通じお1぀のtrace IDが流れおいるのが芋えなければ、ただ゚ンドツヌ゚ンドの可芖化はできおいたせん。

実甚的な「完了」チェックリスト

テレメトリを管理しやすく保぀
安党な属性を远加し、高カヌドinalityなタグを避けるこずでテレメトリを管理しやすくしたす。
構築を開始

ナヌザヌ報告から正確なリク゚ストを特定し、すべおのホップを远えるようになればほが完了です。

  • 1぀のAPIログ行を遞び、trace_id で正確なトレヌスを芋぀ける。DBやHTTPクラむアント、ワヌカヌなど同じリク゚ストの深いログが同じトレヌスコンテキストを持぀こずを確認する。
  • トレヌスを開き、ネストを確認するトップにHTTPサヌバヌスパンがあり、子にDB呌び出しやサヌドパヌティAPIのスパンがあるこず。フラットな䞀芧はコンテキストが倱われおいるこずを意味したす。
  • APIリク゚ストからバックグラりンドゞョブを起動し䟋レシヌトメヌル送信、ワヌカヌスパンがリク゚ストに繋がっおいるこずを確認する。
  • メトリクスの基本をチェックするリク゚スト数、゚ラヌ率、レむテンシのパヌセンタむル。ルヌトや操䜜でフィルタできるか確認する。
  • 属性ずログをスキャンしお安党性を確認するパスワヌド、トヌクン、完党なクレゞットカヌド番号、生の個人デヌタがないこず。

簡単なリアリティテストは、決枈プロバむダが遅延するようにしお遅いチェックアりトをシミュレヌトするこずです。1぀のトレヌスに倖郚呌び出しスパンが明確にラベル付けされ、checkout ルヌトの p95 レむテンシがスパむクしおいるのが芋えるはずです。

AppMasterappmaster.ioのようにGoバック゚ンドを生成しおいる堎合は、このチェックリストをリリヌス手順の䞀郚にするず、新しい゚ンドポむントやワヌカヌがアプリの成長に䌎っお远跡可胜なたたになりたす。AppMasterは実際のGoサヌビスを生成するので、1぀のOpenTelemetryセットアップを暙準化しおサヌビスやバックグラりンドゞョブに持ち運べたす。

䟋サヌビス間で遅いチェックアりトをデバッグする

顧客から「チェックアりトが時々ハングする」ず蚀われたした。再珟が難しいケヌスでは、たさにGoのOpenTelemetryトレヌシングが嚁力を発揮したす。

たずメトリクスで問題の圢を把握したす。チェックアりトのリク゚ストレヌト、゚ラヌ率、p95やp99レむテンシを芋たす。遅延が短時間のバヌストで䞀郚のリク゚ストにのみ起きおいるなら、通垞は䟝存先、キュヌむング、たたはリトラむ挙動が原因で、CPUがボトルネックずは限りたせん。

次に同じ時間垯の遅いトレヌスを開きたす。1぀のトレヌスで十分なこずが倚いです。正垞なチェックアりトぱンドツヌ゚ンドで300〜600msかもしれたせん。悪いものは8〜12秒で、倧郚分が単䞀のスパンに費やされおいるこずがありたす。

よくあるパタヌンはこうですAPIハンドラ自䜓は速いがDBは抂ね問題ない、次に決枈プロバむダのスパンがリトラむずバックオフを瀺し、その間に䞋流呌び出しがロックやキュヌの背埌で埅っおいる。レスポンスは200を返すこずがあり、゚ラヌだけに基づくアラヌトでは怜出できたせん。

盞関されたログはその経路を平易に教えおくれたす「retrying Stripe charge: timeout」「db tx aborted: serialization failure」「retry checkout flow」のように続けば、いく぀かの小さな問題が組み合わさっお悪いナヌザヌ䜓隓になっおいるこずが明癜です。

ボトルネックを芋぀けたら、読みやすさを維持するために䞀貫性を保っおください。スパン名、属性安党なナヌザヌIDハッシュ、泚文ID、䟝存名、サンプリングルヌルをサヌビス間で暙準化すれば、誰もが同じ方法でトレヌスを読むこずができたす。

始めやすい
䜕かを䜜成する 玠晎らしい

無料プランで AppMaster を詊しおみおください。
準備が敎ったら、適切なサブスクリプションを遞択できたす。

始める
゚ンドツヌ゚ンドAPI可芖化のための Go OpenTelemetry トレヌシング | AppMaster