Sentryが明らかにしたこと:可観測性、安定性、自社コードを監視するコストの変化
本番エラーから、スキーマの不一致、トランザクション復旧の失敗、役に立たないログが判明しました。小規模チームがSentryでバックエンド障害を修正し、ブラウザー監視を準備し、アラートのノイズを抑えた方法を紹介します。
以前、可観測性への投資は両極端になりがちでした。誰も見ないダッシュボードをSRE組織全体で整備するか、顧客からメールが届いて初めてサービス停止に気付くかです。2026年には、どちらも許容できません。何が壊れているかを把握する必要がありますが、個人開発の製品にエンタープライズ価格を払わずに知りたいものです。
この記事では、Sentryとの連携で本番エラーをどう発見したか、それを受けて何を変更したか、ノイズと利用量を抑えるため監視をどう設定したかを紹介します。また、小規模な開発チームにとって可観測性ツールがどう変化しているかも見ていきます。
ブラウザーの障害を見逃していた理由
バックエンドは3月からSentryに接続されていました。DSNはGoogle Secret Managerに置かれ、シークレットはCloud Runにマウントされ、起動のたびにsentry_sdk.initが実行されます。メール通知も届いていました。外から見る限り、問題はなさそうでした。
しかし、内部ではそうではありませんでした。フロントエンドはSentryに接続されておらず、ブラウザー例外、ハイドレーションエラー、チャンク読み込み失敗が見えませんでした。さらに、バックエンドはPythonエラーをjavascript-nextjsというプロジェクトに送っていました。組織内にそのプロジェクトしかなかったためです。スタックトレースは誤った場所にたまり、グルーピングは役に立たず、ダッシュボードには複数言語のイベントが混在していました。
個々のバグに取り組む前に、この欠落を解消し、ブラウザー障害とバックエンドエラーを分ける必要がありました。
データから分かったこと
過去90日間のエラーイベントを確認すると、いくつかの傾向が見えました。
自然に解消した過去の障害
対象期間で最も多かったエラーは、SNSサインアップエンドポイントで発生したPostgresのStringDataRightTruncationでした。22秒間に16件です。原因は典型的なスキーマのずれでした。モデル定義では800文字の自己紹介を許可していましたが、本番データベースの列は以前のマイグレーション時のVARCHAR(500)のままでした。ユーザーが500文字を少し超える自己紹介を送信し、Pydantic層は受け入れましたが、INSERTが失敗しました。
列を800文字に拡張するマイグレーションはすでに作成され、翌朝にデプロイされていました。しかし、モデルとDBスキーマがずれても顧客が問題に遭遇するまで気付けないという根本問題は、未解決のままでした。
二つの対策を行いました。まず、SNSサインアップのリクエストスキーマを厳格化し、メールアドレス、表示名、トークンに明示的なmax_lengthを設定しました。異常に長い値はデータベースに届く前に、適切な422として返されます。次に、DB整合性スクリプトを拡張し、information_schema.columns.character_maximum_lengthとアプリケーションが想定する長さを比較するようにしました。今後のマイグレーションで列がモデルの出力可能な長さより短くなれば、次のデプロイで整合性チェックが失敗します。
ロールバックできなかったセッション
一覧の下の方には、一連のエラーがありました。POST /v1/uploadでOperationalError: server closed the connection unexpectedlyが1件発生し、その後同じコード経路からPendingRollbackErrorが続きました。クエリの途中でPostgresが接続を切断し、SQLAlchemyセッションは無効な状態になりました。その上でアップロードサービスが部分結果を保存しようとしました。そのセッションで続く処理はすべて連鎖的に失敗しました。
修正自体は小さいものの、間違えやすい箇所です。アップロードパイプラインが例外を捕捉したら、最初にdb.rollback()を実行します。ロールバックの失敗が元のエラーを覆い隠さないよう、独自のtry/exceptで囲みます。その後、クリーンなセッションで部分保存処理を行います。
内容が消えてしまうログ
WebSocketの終了ハンドラーは、捕捉した例外をerr=%sでログに記録していました。多くの例外では有用な情報が出ます。しかし、一部の例外型では__str__が空文字列を返すため、ログには後続の情報がないerr=だけが出ていました。保存に失敗したことはSentryイベントで分かっても、理由は分かりませんでした。
logger.exception(...)とerr=%rに切り替えてもコストはかからず、今後のイベントに原因が分かる情報が含まれるようになりました。
支払いたくないノイズ
キューに残っていた項目はバグではありませんでした。クライアントがSSL接続中に切断すると、uvloopが時折RuntimeError: unable to perform operation on <TCPTransport closed=True>を発生させます。これは想定どおりの動作で、修正対象ではありません。月5,000件の無料枠を使うだけの価値もありません。そこで、該当する例外と値のパターンを除外するbefore_sendフィルターを追加しました。フロントエンドでもResizeObserver loop limit exceededなど、同様のブラウザーノイズを除外しています。
無料枠に合わせて調整する
2026年10月13日に確認したSentryの現行Developerプランでは、月5,000件のエラー、500万件のトレーススパン、50件のリプレイが無料です。トレーシングは従来のパフォーマンスユニットではなくスパンで計測されます。サンプリング率を決める前に現行プランの上限を確認してください。エラーの上限は、SDKが送信できるすべてのシグナルの予算ではありません。
設定は次のとおりです。
- バックエンドとフロントエンドの両方で
tracesSampleRate: 0にしました。分散トレーシングは確かに便利ですが、月10Kのパフォーマンスユニットでは、悪い午後が一度あるだけで予算を使い切る可能性があります。特定の問題を調べる必要があるときは、低いサンプル率で有効にできます。 - バックエンドでは同じ理由から
profilesSampleRate: 0にしました。 - フロントエンドではSession Replayを無効にしました。連携コードは残したまま、両方のサンプル率をゼロにしました。重大なバグの調査にリプレイが必要なら、
replaysOnErrorSampleRateを1日だけ0.05にし、その後オフに戻せます。 - 両方の実行環境に
before_sendフィルターを設定し、すでに対応不要と分かっているイベントを除外しました。
バックエンドの変更により、対応可能なエラーのノイズは減りました。一方、ブラウザーSDKは有効化に向けて設定を準備した段階です。この二つは異なる進捗です。コードとサンプリング設定があるだけでは、ブラウザーイベントが意図したプロジェクトに届いている証明にはなりません。
可観測性市場で起きている変化
ホスト型サービスとしてはSentryの無料枠が非常に充実しているため、私たちはそこに合わせて調整しました。ただし、経済性が変化しているため、より広い市場の動きも理解しておく価値があります。
ここ12〜18か月で、いくつかの点が変わりました。
- オープン標準は移行作業を減らしますが、すべてをなくすわけではありません。 OTLP互換の計装は監視バックエンドへの依存を弱められます。一方、ベンダー固有のクエリ、ダッシュボード、アラートルール、SDK機能の移行には、引き続き計画とテストが必要です。
- セルフホスティングはコスト計算を変えます。 GlitchTipやOTLPネイティブのスタックなど、別の監視方法もあります。ホスト型プランと比較するときは、VM料金とサブスクリプション料金だけでなく、ストレージ、バックアップ、アップグレード、エンジニアリング時間も考慮してください。
- AI支援デバッグがワークフローを広げています。 SentryのSeerは根本原因やパッチを提案し、認可されたMCP連携はエラーの文脈をコーディングアシスタントに渡せます。提案された修正も、リリース前に再現と回帰テストが必要です。
- プランの上限も機能と同じように確認する必要があります。 エラー量、保持期間、サンプリング、チームアクセスは料金に影響します。必要なシグナルと計測可能なイベント予算から始め、その使用量に合うプランを比較してください。
このリリース時点では、Sentryは直近のエラー監視ニーズを満たしていました。利用可能なオープンインターフェースを使えば将来の選択肢は増えますが、ベンダーを移行するならプロジェクトの振り分け、アラート、保持データ、プロバイダー固有機能を確認する必要があります。
デプロイ確認で監視設定を完成させる
この記事の2026年5月17日時点では、デプロイ関連の作業が二つ残っていました。
- バックエンドエラーが
javascript-nextjsに記録されるのをやめるため、Python専用のSentryプロジェクトを作成する。DSNは、バックエンドがすでに読み取っている同じSecret Managerエントリーで差し替える。 - Vercelのプロジェクト設定にフロントエンドのDSNを設定し、クライアント側エラーが送信されるようにする。コードはすでに存在し、条件に応じて初期化される。足りないのは環境変数だけ。
監視を導入する際は、各実行環境から制御されたテストイベントを送り、連携完了と判断する前に、プロジェクト、環境、グルーピング、コンテキストを確認してください。プロジェクトの分離や環境変数の設定は準備作業であり、イベント到達の証拠ではありません。
なぜ重要なのか
何が壊れているか見えなければ、防御的なコードに過剰投資するか、投資を怠ってバグを出荷します。どちらも高くつきます。安価で信頼できる可観測性があれば、小さなチームでも大きな組織のように動けます。顧客が気付く前に回帰を見つけ、推測せずに修正し、エンジニアリングの時間を新しいものづくりに使えます。
重要なのは、アプリケーションの修正だけでなく、次の回帰を知らせるはずの監視経路も検証することです。