Article by: Lazar Nikolov
要約:バックエンドがすでにOpenTelemetryを使っているなら、環境変数をいくつか変更するだけでトレースとログをSentryへ送信できます。SDKの置き換えも、インストルメンテーションの書き直しも不要です。OTLPエクスポーターの送信先をSentryのエンドポイントに向け、フロントエンドにはブラウザコンテキスト用のSentry SDKを追加するだけで、クリックからバックエンドのスパンまで1本につながったトレースが手に入ります。
バックエンドはすでに OpenTelemetry でインストルメントしている。サービスはスパンを出力している。チームは OTel の API に慣れている。すでに Collector を運用しているかもしれない。そこで Sentry を評価し始めると、当然こんな疑問が生まれます。
OpenTelemetry の構成を Sentry SDK に置き換える必要があるのだろうか。
答えはノーです。
現実的な答えとしては、OpenTelemetry がすでに機能しているところはそのまま使い続け、より多くのアプリケーションコンテキストが得られる箇所に Sentry SDK を追加し、OpenTelemetry Protocol(OTLP)イベントを Sentry に送信する。Webアプリであれば、フロントエンドにはブラウザトレーシング、エラー、ログ、Session Replay、ソースマップのために Sentry SDK を使い、バックエンドの既存サービスインストルメンテーションには OpenTelemetry をそのまま使う、というケースが多いです。
スコープの補足:OTLPはトレース、ログ、メトリクスを扱えます。現時点では、SentryのOTLPインジェストはログとトレースをサポートしており、メトリクスはサポートしていません。将来的にサポートを追加することを検討しています。
重要なのは、よく混同されがちな2つの判断を分けて考えることです。
- フロントエンドとバックエンドでトレースをどのようにつなげるか。
- バックエンドのOTLPイベントをどのようにSentryへエクスポートするか。
この2つを分けて考えると、アーキテクチャがずっとシンプルに整理できます。
Sentry vs OpenTelemetry は問い方が間違っている
最初の判断はトレースの連結です。ユーザーがReactアプリのボタンをクリックし、そのクリックがバックエンドへのリクエストをトリガーする場合、フロントエンドとバックエンドは同じ分散トレースコンテキストで合意する必要があります。この例では、Sentry フロントエンド SDK が W3C の traceparent ヘッダーを送信し(propagateTraceparent オプションで設定可能)、OpenTelemetry バックエンドがそのトレースを引き継ぎます。
この連結はフロントエンド SDK の設定で処理されます。

2つ目の判断はエクスポートです。バックエンドがテレメトリを生成した後、それらのOTLPイベントはどこへ送られるのか。
一般的な選択肢は2つあります。
- バックエンドからSentryのOTLPエンドポイントへ直接OTLPイベントを送信する。
- OTLPイベントをOpenTelemetry Collectorへ送り、CollectorがSentryのOTLPエンドポイントへ転送する。
このトレース継続のステップがあるからこそ、Sentryでインストルメントされたブラウザのアクションが、どちらのOTLPエクスポートオプションを選んでも、バックエンドのOpenTelemetryの処理の親スパンになれるのです。
これらの詳細なリファレンスドキュメントは、Sentry SDKとOpenTelemetry SDKの連携、OpenTelemetryトレースをSentryへ直接送信する方法、OpenTelemetryログをSentryへ直接送信する方法、OpenTelemetryデータをSentryへ転送する方法をご参照ください。
直接OTLPとCollector転送の比較
直接OTLPとCollector転送はどちらも最終的にSentryのOTLPエンドポイントへ到達します。違いは、サービスがSentryに直接送信するか、まずCollectorを経由するかです。
| アプローチ | 適しているケース | 得られるもの | トレードオフ |
|---|---|---|---|
| 直接OTLPをSentryへ | バックエンドサービスが1つ、またはプロジェクトが1つで、最小限の構成が望ましい場合 | 構成要素が少なく、サービスからSentryまでの経路が短い | 処理・サンプリング・ルーティングの中央管理がしにくい |
| Collector転送 | 複数のサービスがある、すでにCollectorを運用している、データ処理が必要、またはマルチベンダーへのルーティングが必要な場合 | ルーティング、バッチ処理、データ加工、サンプリングの一元管理と、ベンダー評価のしやすさ | デプロイと運用が必要なコンポーネントが増える |
単一のバックエンドプロジェクトであれば、直接OTLPが最もシンプルです。

Collector転送は、オブザーバビリティの構成が「1つのアプリが1つの送信先に送る」という規模を超えている場合に適しています。複数のサービスからテレメトリを1か所で受け取り、バッチ処理して加工し、1つまたは複数のバックエンドに送信できます。
最後の点は、Sentryを評価する際に特に重要です。既存のベンダーへのルーティングを維持しながら、コピーをSentryにも転送することで、バックエンドのインストルメンテーションを書き直さずにデバッグ体験を比較できます。
大規模な構成を使う場合に、Sentry固有の重要な注意点が1つあります。汎用のOTLP HTTPエクスポーターは、1つのプロジェクトキーを持つ1つのSentryプロジェクトエンドポイントを指します。すべてのサービスをその1つのエクスポーター経由で送ると、すべてのサービスが同じSentryプロジェクトに入ってしまいます。複数プロジェクトへのルーティングには、Sentryエクスポーターを使用してください。service.name などのリソース属性に基づいてOTLPイベントを各プロジェクトへルーティングでき、適切なSentry APIパーミッションを設定すれば存在しないプロジェクトを自動作成することも可能です。
デモアーキテクチャ
Collector転送を使ったデモプロジェクトを見てみましょう。

フロントエンドは frontend/ にあります。@sentry/react を使ったReact + Viteアプリです。バックエンドは backend/ にあり、OpenTelemetry SDK、FastAPIインストルメンテーション、SQLAlchemyインストルメンテーション、手動スパン、そしてチェックアウトロジックの標準ロギングを使ったFastAPIサービスです。Collectorは collector/ にあり、これらのOTLPイベントをSentryへ転送します。
このデモのポイントは、すべてのレイヤーで同じSDKを使うことではありません。すべてのレイヤーが同じトレースに参加し、バックエンドのログがそのデバッグコンテキストに関連付けられること、それがポイントです。
フロントエンドはSentry SDKを使用する
Sentryの設定は frontend/src/instrument.ts にあります。ブラウザトレーシング、Session Replay、ログ、トレース伝播を有効にしています。

フロントエンドのチェックアウトフローは frontend/src/hooks/useCheckoutLab.ts にあります。ユーザー向けの処理にSentryスパンを作成し、有用な状態変化をログに記録し、予期しないエラーをキャプチャします。

実際のリクエストは通常の fetch 呼び出しです。

このリクエストにはトレースヘッダーを手動で追加するコードはありません。送信先が tracePropagationTargets に一致していれば、Sentryのブラウザトレーシングインテグレーションが自動的に伝播を処理します。
バックエンドはOpenTelemetryをそのまま使用する
バックエンドはSentry SDKをインストールも初期化もしません。OpenTelemetryの設定は backend/app/core/observability.py にあります。
OpenTelemetry の TracerProvider を作成し、サービスのリソース属性を付与し、OTLP HTTP経由でスパンをエクスポートし、W3Cトレースコンテキスト伝播を登録します。

OTLPログのエクスポートも設定します。バックエンドは LoggerProvider を作成し、OTLPLogExporter をアタッチし、アプリのロガーに OpenTelemetry の LoggingHandler を追加します。

デフォルトのバックエンドエンドポイントはローカルです。

つまりバックエンドは、OTLPのトレースとログをSentryへ直接ではなく、Collectorへエクスポートします。FastAPIアプリはCORSでブラウザのトレースヘッダーも通過させます。

これは見落としやすいポイントです。ブラウザがクロスオリジンリクエストを送っていて、CORSが伝播ヘッダーをブロックしてしまうと、両側が正しくインストルメントされていても、フロントエンドとバックエンドのトレースが分断されることがあります。
チェックアウトフローで手動OTelスパンとログを追加する
backend/app/services/checkout.py のバックエンドサービスコードは、チェックアウトのワークフローをモデル化しています。ビジネス処理に対して手動でOpenTelemetryスパンを作成しています。

チェックアウトフローには次のスパンが含まれます。
checkout.validate_cartcheckout.reserve_inventorycheckout.calculate_taxcheckout.paymentcheckout.write_ordercheckout.send_confirmation
同じビジネスステップに対してバックエンドのログも出力されます。checkout.started、checkout.cart_validated、checkout.inventory_reserved、checkout.payment_approved、checkout.order_written、checkout.completed といったログです。これらはPythonの標準 logging API を通り、OpenTelemetry の LoggingHandler がOTLP経由でエクスポートします。
OTelファーストなチームが最も気にするのはここです。これらのスパンとログはOpenTelemetryのパイプライン内に留まります。Sentryでトレースと関連ログを確認するために、Sentry.startSpan() やSentryのロギングAPIに書き直す必要はありません。
CollectorがOTLPをSentryへ転送する
Collectorの設定は collector/otel-collector.yaml にあります。gRPCとHTTPでOTLPを受け取ります。

データをバッチ処理します。

そしてOTLP HTTPエクスポーターでSentryへ転送します。

補足:このデモでは単一のSentryプロジェクト向けに汎用の otlphttp を使用しています。複数プロジェクトへのルーティングやプロジェクトの自動作成が必要な場合は、sentry エクスポーターに切り替えてください。
設定されたパイプラインはデバッグエクスポーターとSentryの両方に送信します。

これがCollector転送パターンです。バックエンドはOTLPイベントを1つのローカルエンドポイントへ送り、Collectorがそのテレメトリをどこへ送るかを決定します。
各レイヤーの責務
このアーキテクチャを理解する最も分かりやすい方法は、オーナーシップで整理することです。
フロントエンドのSentry SDKが担う、ブラウザ固有のデバッグコンテキスト
- ブラウザスパンとフロントエンドのトランザクション
- フロントエンドのエラーとReactエラーバウンダリ
- Session Replay
- フロントエンドのログ
- Sentry Viteプラグインによるソースマップ
- バックエンドへのトレース伝播
バックエンドのOpenTelemetry SDKが担う、バックエンドのインストルメンテーション
- FastAPIのリクエストスパン
- SQLAlchemyのスパン
- バックエンドが他サービスを呼び出す場合のHTTPXスパン
- 手動チェックアウトスパン
- OpenTelemetryでエクスポートされるバックエンドのログ
service.nameやdeployment.environmentなどのリソース属性- CollectorへのOTLPエクスポート
Collectorが担う、ルーティングと処理
4317と4318でのOTLP受信- テレメトリのバッチ処理
- ローカルでのデバッグ出力の表示
- SentryのOTLPエンドポイントへの転送
- サンプリング、変換、フィルタリング、マルチベンダールーティングを後から追加できる場所の提供
SentryとOpenTelemetryが競合する選択肢ではない理由はここにあります。両者は同じオブザーバビリティパイプラインの中で、異なる役割を担っているのです。
Sentryで確認できること
デモでは、同じフロントエンドセッションからチェックアウトシナリオを実行しました。Sentryには、Reactで始まりFastAPIバックエンドへと続く1本の連結されたトレースとして表示されます。通常のチェックアウト、遅い支払い、在庫なし、支払い拒否、バックエンドクラッシュといった処理が、ツールを切り替えることなく同じ分散トレース上で確認できます。

checkout.started、checkout.inventory_reserved、checkout.payment_slow_path、checkout.completed といったログは、Pythonの標準ロギングAPIから出力され、OTLPでエクスポートされ、スパンと同じデバッグコンテキストに関連付けられた状態でSentryに届きます。
自分のアプリへの適用判断フロー
直接OTLPをSentryへ送る場合
- バックエンドサービスまたはプロジェクトが1つである。
- 構成要素をできるだけ少なくしたい。
- 中央処理やマルチ送信先へのルーティングがまだ必要ない。
Collector転送を使う場合
- すでにOpenTelemetry Collectorを運用している。
- バックエンドサービスが複数ある。
- サービスを別々のSentryプロジェクトに振り分ける必要がある。
- アプリプロセスの外でサンプリング、フィルタリング、変換、バッチ処理が必要。
- Sentryを評価しながら既存ベンダーにもテレメトリを送り続けたい。
SentryのバックエンドSDKを後から追加する場合
- バックエンドのエラーや例外をSentry SDKでキャプチャしてトレースに関連付け、例外からリクエスト全体の経路へジャンプして関連するログ、メトリクス、プロファイル、スパンを確認したい。
- プロファイリングが必要。
- Sentry の Application Metrics を使いたい。
- 現在のOpenTelemetryデータには含まれていないSentryの機能が必要。
最後のステップは任意であり、前提条件ではありません。まずフロントエンドにSentryを、バックエンドにOpenTelemetryを使い始めて、バックエンドにSentry SDKを追加する価値があるかどうかは後で判断できます。
トレースを維持できる最小限の変更から始める
バックエンドがすでにOpenTelemetryを使っているなら、インストルメンテーションの書き直しから始めないでください。まずOTLPイベントをどこへ送るかを決めることから始めましょう。
バックエンドが1つであれば、直接OTLPをSentryへ送るだけで十分です。複数のサービスがある場合、ベンダー評価中の場合、またはルーティングや処理が必要な場合は、間にCollectorを置きましょう。そしてW3C traceparent 伝播でフロントエンドのSentry SDKとバックエンドのOTel SDKを接続してください。これにより、最も価値ある部分をまず手に入れられます。ユーザーのアクションが起きた場所から始まり、すでにインストルメントしているバックエンドコードへと続く、1本のトレースです。
SentryかOpenTelemetryかを選ぶ必要はありません。それぞれが適した場所で両方を使いましょう。
FAQ
■ Sentryを利用するためにOpenTelemetryを削除する必要がありますか?
いいえ。バックエンドがすでにOpenTelemetryでインストルメントされているなら、そのまま使い続け、OTLPイベントをSentryへ送信してください。フロントエンドのエラー、Session Replay、ブラウザトレーシング、ログ、ソースマップなど、より多くのコンテキストが得られる箇所にSentry SDKを追加してください。
■ CollectorではなくOTLPを直接送るのはどんな場合ですか?
バックエンドサービスやプロジェクトが1つで、できるだけシンプルな構成が望ましい場合です。複数のサービスがある、処理やテールサンプリングが必要、またはテレメトリを複数のバックエンドにルーティングしたい(Sentryを評価する際に有効)場合はCollectorを使用してください。
■ バックエンドがSentry SDKではなくOpenTelemetryだけを使う場合、何が失われますか?
バックエンドのOTLPイベントは引き続きSentryへ送信できます。プロファイリングやSDK固有のコンテキストといった機能は、後からSentryバックエンドSDKまたはOpenTelemetryインテグレーションを追加することで利用できます。
■ SentryはOTLPのメトリクスを受け取れますか?
Original Page: You don’t need to pick one: how Sentry and OpenTelemetry work together
IchizokuはSentryと提携し、日本でSentry製品の導入支援、テクニカルサポート、ベストプラクティスの共有を行なっています。Ichizokuが提供するSentryの日本語サイトについてはこちらをご覧ください。またご導入についての相談は「お問い合わせ」からお気軽にお問い合わせください。

