Article by: Sergiy Dybskiy (読了時間:12分)
コードをインストルメントするとき、「ログを使うべきか、トレースか、メトリクスか」という問いに何度もぶつかります。コーディングエージェントも同じ壁にぶつかっているのを目にします。答えは明白なように思えますが、実はそうでもありません。エラー・トレース・ログ・メトリクス は、アプリの大半を支える4種類のテレメトリです。
4つはいずれも同じ道具箱に入ったツールであり、役割も重なる部分が多いため、正直なところ答えは開発者の口癖である「場合によります」です。ログに記録するはずのコンテキストをスパンの属性として持たせることもできますし、メトリクスを出力する代わりにログイベントをカウントすることもできます。また、duration を追加してスパンと見なすこともできます。
[スパイダーマンのミームを入れようとしたのですが、法務部門から著作権侵害になると言われたので削除しました。]
ただし、「できる」と「すべき」は別の話です。各シグナルは異なる問いに答えるために存在しており、届いた後のワークフローもそれぞれ異なります。しっかりとした指針がなければ、慣れ親しんだものや既にあるものに手を伸ばしてしまい、他の種類のシグナルが本来何のためにあるかを見落としてしまいます。
この記事は、自分自身とエージェントのために書きたかった指針です。スキルだけ欲しい方は末尾まで読み飛ばしてください。
Sentry では、エラー・トレース・ログ・メトリクスのすべてがひとつの SDK から取得でき、すべてのプランに含まれています。エラーと Sentry Tracing は長年使われてきた機能ですが(それぞれ 2012年 と 2020年 から)、構造化ログは昨年追加され、Application Metrics は今年5月にラインナップが完成しました。Sentry をしばらく使っているなら、エラーとトレースはすでに送信されているはずです。ログとメトリクスは、テレメトリの全体像を完成させるための、残りの道具です。
エラー・トレース・ログ・メトリクス:それぞれが答える問い
エラー:「何が壊れたのか」
トレース:「リクエストは想定どおりに送信されたのか」
トレースは、タイミング付きのスパンのウォーターフォールです。リクエストがサービスを横断して、どのように送信されたかを追跡し、時間の使われ方を把握するためのものです。遅くなったDBクエリ、タイムアウトしたAPI呼び出し、200msのはずが8秒かかったLLMのツール呼び出しなどが見えてきます。
メトリクス:「時系列でどう推移しているのか」
Counter・Gauge・Distribution として保持され、任意の属性でスライスでき、集計値から背後にあるサンプル(およびトレース)まで掘り下げられます。「今週のチェックアウト件数は12,000件」という数字だけでなく、米国から8,400件・EUから2,600件・その他から1,000件という内訳も、直近のデプロイ前後で数値がどのように推移したかも把握できます。メトリクスは現在だけでなく過去のシグナルでもあるため、ダッシュボードやアラートとの相性が抜群です。(ただし Sentry ではほぼすべてのシグナルにアラートを設定できます。)
ログ:「コードのこの箇所で何が起きていたのか」
コードのある瞬間のシステム状態を構造化イベントとして記録したものです。設定値・フィーチャーフラグ・関数の入出力・ユーザーIDなどが含まれます。ログは関数の意思決定ツリーをたどる手掛かりです。コードが分岐する地点に残すマーカーであり、後から人間やエージェントがその判断の流れを追えるようにします。エラーとトレースが「何が壊れたか」「どこに時間がかかったか」を教えてくれた後、「なぜ」を補うのがログです。
実例(に近いもの)
React フロントエンドと Python API を持つストアフロントを運営しているとします。サポートからチケットが転送されてくるようになりました。アカウントページの商品レコメンドが、ログイン済みの一部ユーザーに対して汎用的な内容(パーソナライズされたおすすめではなくベストセラー)を表示しているというのです。どうも様子が変です。
何かクラッシュしていないか?
最初に確認するのは Issues です。React アプリに例外はなく、リクエストも失敗しておらず、/recommendations/{user_id} への呼び出しはすべて 200 を返しています。エラー追跡の観点からは、アプリは完全に健全です。
処理が遅くなっていないか?リクエストが想定外の経路をたどっていないか?
影響を受けたリクエストのうち1件のトレースを取得します。ルートと DB クエリは自動インストルメントされており、レコメンドのステップには名前付きスパン をいくつか追加しています。

リクエストはユーザーを読み込み、ranking_v2 フラグを評価し、recommendations_v2 を照会し、人気アイテムへフォールバックして、ランキングを行っていました。経路は正しく、所要時間にも問題ありません。recommendations_v2 クエリは「成功」しています(0件を返すことも、クエリとしては完全な成功です)。
そのため、コードは設計どおりに動き、フォールバックしました。トレースはリクエストが設計どおりに流れたことを教えてくれます。しかし、その設計がこのユーザーに対して静かに失敗していることは教えてくれません。表面上はすべて正常です。
もう少し深く掘り下げられないのか?
チケットに記載されたユーザーでログを検索すると、ハンドラー内の構造化ログが、フォールバックを判断した瞬間の状態を教えてくれます。

このユーザーは ranking_v2 フィーチャーフラグのバケットに入っており、新しい recommendations_v2 テーブルからパーソナライズされたおすすめを読み込む設定になっています。テーブルはデプロイされましたが、行が一度もバックフィルされなかったため、参照結果は空でした。コードにとって空の結果は「このユーザーにパーソナライズされたレコメンドはない」という完全に正当な状態です。これは、閲覧履歴のない新規ユーザーの場合と同じ扱いです。そのため、ベストセラーにフォールバックして 200 を返しました。
ではスパンにこのデータを持たせるだけでよかったのでは?
outcome や candidate_count をスパン属性として設定することはできます。しかしトレースはサンプリングされることがあります。そのため、顧客から問い合わせのあった「まさにそのリクエスト」が、少なくとも私の経験では、サンプリングの対象外になっていることも少なくありません。スパン属性は見つけたトレースを理解するには役立ちますが、トレース自体を見つける助けにはなりません。一方、ログはサンプリングされません。
何人のユーザーが影響を受けているのか?
影響を受けているユーザーが1人だけであれば、サポートチケットとして対応すれば済みます。ごく一部なのか、それとも相当な割合なのかによって、月曜に修正するか今夜オンコールを呼ぶかが変わります。ranking_version と outcome でタグ付けした recommendations.served の Counter が、その判断材料になります。

v2 パスはほぼすべてフォールバックを返しており、v1 は正常で、落ち込みはフラグのロールアウトと一致しています。トレースを1件も開かずに、影響範囲と原因が分かります。
問題を解決したのは、どれか一つのシグナルではありません。それぞれのシグナルが、可能性を一つずつ絞り込んでくれました。Issues にエラーがないことから、クラッシュではないと分かります。metric によって、一時的な事象ではなく、v2 コホート全体がフォールバックしていることが判明しました。トレースは(サンプリングされたものが見つかれば)、リクエストが設計どおりのパスを通っていることを示していました。だからこそ、問題は見過ごされていたのです。最後に、チケットに記載された user_id をもとにログを確認すると、「なぜそうなったのか」が分かりました。トレースを詳しく追わなくても、原因にたどり着けたのです。
いつ何を使うか
これが私の判断軸です。
| 知りたいこと | 使うシグナル |
|---|---|
| 何かクラッシュした。スタックトレースを見たい | エラー |
| どれくらい時間がかかったか?どのステップが遅いか? | トレース |
| リクエストは想定どおりのステップを通ったか? | トレース |
| コードがこの判断をしたとき、どのような状態だったか? | ログ |
| この関数は何を受け取り、何を返したか? | ログ |
| X はどれくらいの頻度で発生しているか?レートは正常か? | メトリクス |
| デプロイ後に何か変化はあったか? | メトリクス |
境界線が曖昧になるケースもあります。同じ値が複数のシグナルに現れることもあるため、使い分けにはニュアンスがあります。
スパン属性 or メトリクス?
リクエストがシステム内をどのように流れたかというコンテキストを、トレースを見ながら確認したいなら span 属性です。ウォーターフォールの span に表示されます。スタンドアロンの値として、チャート化・アラート設定・時系列でスライスしたいならメトリクスです。同じ数値が両方に値することもあります。candidate_count を span 属性にすれば1件のリクエストを読むときに使えますが、recommendations.served をメトリクスにすれば全リクエストのレートを監視できます。一方は個別のフローを検査するため、もう一方は集計値を見るためのものです。
ログ or span?
span はフローの中のタイミング付きノードであり、ほとんどは自動インストルメントされるため、自分で書くことはほぼありません。ログは、そのノード内で行われた意思決定時の状態であり、常に意図的に書きます。span は「どこで」「どれくらい時間がかかったか」に答え、ログは「その時点で何が起きていて、なぜその判断になったのか」に答えます。
ログ or メトリクス?
ログは1件のリクエストのストーリー、つまり「針」です。メトリクスは集計値、つまり「干し草の山が正常かどうか」の問いです。おかしくなった特定のリクエストを探したいときはログ。おかしくなったリクエストが何件あるかを知りたいならメトリクスです。
エラー or ログ?
スタックトレースが必要で Issue として追跡すべきなら エラー。予期しない状況だが処理済みで記録する価値があるなら ログ。本当に軽微な問題なら logger.warning(exc_info=True) でトレースバックをログに含めつつ、エラーフィードをノイズで埋めずに済みます。
インストルメントの実装例
上記で紹介したシグナルは、すべてひとつのエンドポイントから得られたものです。ここでは、ウォークスルーで使用した GET /recommendations/{user_id} を例に見ていきます。このエンドポイントでは、ユーザーを読み込み、ranking_v2 フラグを確認し、recommendations_v2 を照会したうえで、結果が空なら人気アイテムへフォールバックします。以下は、そのハンドラーにインストルメントを追加した実装例です。
ほとんどのインストルメンテーションは、自分で書く必要はありません。FastAPI インテグレーションがリクエスト全体をトレースし、データベースインテグレーションがすべてのクエリをトレースするため、手書きの span を1行も追加しなくても、リクエストの経路と各処理に要した時間が取得できます。
自分で追加するのは、意図的に残したいシグナルだけです。具体的には、フローを補完する span 属性を1〜2個、意思決定を記録するログ、そして集計のためのメトリクスを追加します。

意図的に追加するのは3つだけです。それぞれが、ほかでは担えない役割を果たします。span 属性は、リクエストのフローにランキングパスを紐づけるため、トレースを開けばすぐに確認できます。ログは関数が何を判断し、なぜそう判断したのか、その判断を行った瞬間に記録します。メトリクスは後からスライスできるよう、十分なディメンションを持たせてアウトカムを集計します。
ウォーターフォール上でサブ処理の所要時間を計測したい場合(たとえばランキング処理や、外部レコメンダーの呼び出しなど)は、sentry_sdk.start_span を使ってカスタム span でラップできます。
自分で書くコード以外にも、SDK が自動的に補完してくれる情報は豊富です。フロントエンド SDK は、ブラウザ、OS、リリース情報を自動で付与します。sentry_sdk.set_user() を一度呼ぶだけで、そのユーザー情報はリクエストのエラー、span、ログ、メトリクス全体に引き継がれます。また、4つのシグナルはすべて同じ SDK から生成されるため、共通の trace_id を持ち、自動的に関連付けられます。すべてのログには対応するトレースが含まれており、メトリクスのスパイクから、その背後にあるトレースに直接ジャンプできます。そこに至るまでに、4つのベンダーをつなぎ合わせる必要はありません。

これらはすべてすぐに使え、全プランに含まれています。意図的なシグナル(span 属性・意思決定ログ・メトリクス)は自分で配置するものであり、前もって、コードが後から振り返って検証する価値のある判断を下す箇所に仕込んでおいてこそ機能します。
適材適所
上記の切り分けは概念的なものにとどまりません。API の設計にも反映されており、それぞれが用途に合わせてチューニングされています。Metrics API はカウントと計測値を集計用に送信するために設計されています。span API は処理時間とリクエストの流れ(構造)を計測するために設計されています。log API はお好みの構造化ログライブラリと統合されており、既に書いているログ行がクエリ可能なイベントになります。ワークフローに合った API を選ぶことは、多くの場合、持っている値の「種類」に合った API を選ぶことを意味します。カウント値・処理時間・ある瞬間の状態、それぞれに対応する API があります。
サンプリングの考え方も同じロジックから導き出されます。トレースはトラフィックの代表サンプルとして最もよく機能します。時間の使われ方を把握するためにすべてのリクエストは必要なく、一定割合で十分です。(コストも抑えられます。)ログはその逆で、すべて保持します。なぜなら目的は「おかしくなった」稀なリクエストを見つけることであり、サンプリングで落とした対象は探せないからです。メトリクスもサンプリングされません。ログと同様、before_send_metric でフィルタリングします。問いに合わせて保持ポリシーを選びましょう。「時間がどこへ消えるか」には代表サンプルを、「このリクエストに何が起きたか」にはすべてのイベントを、というように。
コードベースをデバッグするのはもう自分だけではない
Modem のCody は、AI エージェントがどこに時間を使っているかを把握するためにインストルメントを追加しました。Codex と協力し、非同期処理や論理的な処理単位(たとえばモデルを呼び出す前に行われる一連の処理)を span でラップしました。キャッシュヒット数や time-to-first-token は、時系列で監視できるメトリクスにしました。一方、特定の span の中でのみ意味を持つ値は span 属性として残し、「ここでこれが起きた」という軽量なマーカーはログにしました。span 属性にするか metric にするかの判断が毎回明確だったわけではありませんが、彼のルールは「ある span のコンテキストでのみ意味を持つ値なら、その span に置く」というものでした。
トレーシングを整備したあと、Cody は MCP サーバー経由で Codex が Sentry のデータを参照できるようにし、さらに開発環境で実行した Playwright テストの実データを与えたうえで、「このコードパスを最適化せよ」という目標を与えました。エージェントは span を読み、並列実行できる処理を見つけ、結果が実際に必要になるまで await しないようコードを書き換えました。
これが可能だったのは、トレースが各ノードにタイミング情報を持つ、構造化された依存関係ツリーという、エージェントがそのまま推論に利用できる形式だからです。同じ情報をログの並びとして渡した場合、エージェントはまずタイムスタンプと文字列マッチングを頼りに、コールグラフを再構成しなければなりません。
「ワイドイベント」はどうなのか?
4つのシグナルは過剰だという議論があります。リクエストごとにひとつのリッチなワイドイベントを送信し、後から残りを導き出せばよい、という考え方です。これは半分だけ正しい主張です。
ワイドに送る、それは間違いなくそのとおりです。どのシグナルも、理想的にはコンテキストを豊富に持った構造化イベントであるべきです。有効だったフラグ、ユーザー、入出力などを含み、生の数値や1行の文字列だけで終わるべきではありません。
ただし、送信する「形」が、後で使える「形」になります。カラム型ストアに保存したワイドイベントは、後からチャートを作ることはできます。しかし、それだけでは重複排除された Issue にグルーピングされたり、ウォーターフォールとして描画されたり、まだ定義していない閾値に基づいてリアルタイムアラートを発火したりはできません。それらはワークフローであり、それぞれが特定のデータの形を必要とします。
だから、幅広い情報を送りつつも、自分が実際に必要とするワークフローに対応したシグナルへ送信してください。ハンドラーがメトリクスとログの両方を送信するのはそのためです。同じ判断、同じトレースから、2つの形が生まれます。レートを監視することと、1件のリクエストを再構成することは、異なる仕事だからです。
はじめ方
ログとメトリクスは、まだ有効にしていないかもしれない2つのシグナルです。Sentry としては比較的新しい機能であり、まだ利用していない方も多いでしょう。どちらも全プランに含まれています。
手作業で配線する必要はありません。コーディングエージェントを Sentry のセットアップスキルに向ければ、お使いのスタックに合わせて SDK のインストール、トレース・ログ・メトリクスの有効化、さらに意思決定ポイントへのインストルメントの配置まで行います。その後は、MCP サーバー経由でエージェントに Sentry のデータを参照させ、実際のデータを与えてください。たとえば、最も遅いトレースや最新の Issue などです。
判断フレームワークだけが欲しい方には、それ単体のスキルとして用意されています。

デバッグのために自分が送信するテレメトリは、エージェントが手助けのために読み取るテレメトリと、同じものです。
FAQ
■ ログ・トレース・メトリクス はいつ使い分けるべきですか?
どこに時間がかかっているかや、リクエストが設計どおりに流れたかを確認したいときは トレース(ほとんどは自動インストルメント)。特定の意思決定ポイントでの状態と「なぜ」を知りたいときはログ(サンプリングされないため、特定のリクエストを必ず追えます)。全リクエストにわたるレートや傾向を知りたいときは メトリクス。スタックトレースが必要で、解決まで追跡すべきものはエラーです。
■ すべてログに記録して、トレースとメトリクスは省いてもいいのでは?
できますが、他のシグナルが提供するワークフローを失います。トレースは人間やエージェントが推論できるウォーターフォールとして描画されます。メトリクスはチャート化やアラート設定ができる低コストな集計データです。エラーはスタックトレース付きで重複排除された Issue にグルーピングされます。ログの件数を数えてレートを導き出そうとすると、メトリクスとして直接送れば済む数値のために、すべてのログを保持するコストを払うことになります。
■ 4つのシグナル、最初から全部必要ですか?
いいえ。エラーは SDK を初期化した瞬間から収集されます。span の約80%はフレームワークとデータベースインテグレーションによって自動インストルメントされます。意図的に追加するのは残りの20%だけです。後から振り返って検証する価値のある判断を下す場所に、意思決定ログといくつかのメトリクスを追加するだけです。
■ 各シグナルのサンプリングはどう考えればよいですか?
サンプリングするのはトレースだけです。traces_sample_rate を設定して代表的なサンプルを保持します(開発環境では高め、本番環境では低め)。エラーはデフォルトで収集されます。ログとメトリクスはサンプリングされません。ランダムに間引くのではなく、before_send_log と before_send_metric でフィルタリングします。送信したログとメトリクスはすべて保持され、それぞれのトレースに紐付けられます。だからこそ、おかしくなった1件のリクエストを必ず見つけられ、メトリクスから、その背後にあるサンプルまで掘り下げられるのです。
Original Page: Errors, traces, logs, metrics: when to reach for what
IchizokuはSentryと提携し、日本でSentry製品の導入支援、テクニカルサポート、ベストプラクティスの共有を行なっています。Ichizokuが提供するSentryの日本語サイトについてはこちらをご覧ください。またご導入についての相談は「お問い合わせ」からお気軽にお問い合わせください。

