Sentry の電話アラート:本番を壊すエラーだけ鳴らす

Sentry に電話アクションはありません。Webhook 経由でイシューアラートを流し、本番を壊すエラーだけを着信として鳴らす設定・ペイロード・絞り込み方。

更新日

目次

Sentry はあなたに電話をかけられません。メールを送ることも、Slack に投稿することも、PagerDuty にアラートを渡すこともできますが、音声アクションは組み込まれていません。インシデント管理プラットフォームを買わずにそれを手に入れる方法は、Sentry のイシューアラートを「鳴る Webhook」に送ることです。内部インテグレーションを作り、Echobell の着信チャンネルに向け、その手前にフィルタを置いて、本当に本番を壊すエラーだけを通します。

この記事では経路全体を扱います。インテグレーション、アラートルール、Sentry が実際に送ってくるペイロード、それを読むテンプレート、そして多くの人がここで諦める 2 つの落とし穴です。

なぜ Sentry 自身は電話を鳴らせないのか

Sentry のイシューアラートのアクションは、通知(メール、Slack、Discord、Microsoft Teams)、チケット作成(Jira、GitHub、Azure DevOps)、ページング製品への引き渡し(PagerDuty、Opsgenie)に分かれます。どれも行き着く先は「見ていないと気づかない画面」か、「別プラットフォームの有料シート」です。

14 時ならそれで構いません。しかし午前 3 時、Slack メッセージは沈黙と区別がつかず、プッシュ通知はおやすみモードに負けます。2 時間の遅れが実際の損失になる少数のエラー——決済が 500 を返す、認証が全ログインを弾く、ワーカーが黙ってジョブを捨てている——には、鳴るデバイスが要ります。

その継ぎ目が Webhook です。Sentry はアラートルールのアクションとして任意の HTTPS エンドポイントを呼べます。Echobell はその HTTP リクエストを、iOS の集中モードを突破する着信型アラートに変えます。

必要なもの

  • Settings → Developer Settings に入れる Sentry 組織(owner か manager)
  • Echobell のインストール(App Store / Google Play)
  • 5 分

あなた側にインターネットから到達可能なものは何も要りません。送信するのは Sentry で、あなたは受け取るだけです。

ステップ 1 — 鳴るチャンネルを作る

Echobell でチャンネルを作り、通知タイプを 着信(Calling) にします。ここがこの作業の要点です。着信チャンネルはプッシュではなく着信として振る舞うので、集中モードやおやすみモードを突破します。

午前 3 時でも意味が通るテンプレートにしてください。Sentry のペイロードは深くネストしているため、変数のパスは普段より長くなります。

タイトル: {{data.event.level}}: {{data.event.metadata.type}}
本文: {{data.event.title}} — {{data.event.culprit}}

詳細設定で リンクテンプレート を設定しておくと、通知をタップしてイシューを開けます。

{{data.event.web_url}}

チャンネル詳細画面から Webhook URL をコピーします。形式はこうです。

https://hook.echobell.one/t/<channel-token>

ステップ 2 — Sentry の内部インテグレーションを作る

Sentry が Webhook をアラートルールのアクションとして提供するのは インテグレーション 経由だけなので、作る必要があります。とはいえフォームを埋めるだけで、サービスを書くわけではありません。コードは一行も要りません。

  1. Settings → Developer Settings → Custom Integrations へ
  2. Create New Integration → Internal Integration
  3. Name:Echobell(アラートルールで選ぶときの名前になります)
  4. Webhook URL:ステップ 1 のチャンネル URL
  5. Alert Rule Action のトグルを オン に
  6. Permissions:Issue & Event → Read で十分
  7. Webhooks のチェックボックスは すべて外したまま ——理由は後述の落とし穴で
  8. 保存

内部インテグレーションは自組織限定で、自動的にインストールされます。生成されるトークンはこの構成では使いません。

ステップ 3 — アラートルールのアクションに追加する

Alerts → Create Alert → Issue Alert に進むか、既存ルールを編集します。

Then perform these actions で Send a notification via an integration を追加し、Echobell を選びます。

Action interval(「このアラートが複数回発火したとき」のスロットル)は最低でも 30 minutes にしてください。既定は毎回送信で、毎分 400 回発火するエラーは、あなたが機能を切るまで電話をかけ続けます。

保存したら、ルールのテストを実行し、実際のペイロードが届くのを見てから信用してください。

ステップ 4 — 実際に何が届くのかを把握する

多くの構成がここで壊れます。ペイロードの形が想像と違うからです。Sentry はすべてを包みます。

{
  "action": "triggered",
  "actor": { "id": "sentry", "name": "Sentry", "type": "application" },
  "data": {
    "event": {
      "event_id": "e4874d664c3540c1a32eab185f12c5ab",
      "level": "error",
      "title": "ReferenceError: heck is not defined",
      "culprit": "?(<anonymous>)",
      "platform": "javascript",
      "project": 1,
      "release": null,
      "metadata": { "type": "ReferenceError", "value": "heck is not defined" },
      "tags": [["level", "error"], ["browser", "Chrome 75.0.3770"]],
      "issue_id": "1117540176",
      "issue_url": "https://sentry.io/api/0/issues/1117540176/",
      "web_url": "https://sentry.io/organizations/test-org/issues/1117540176/events/e4874.../"
    },
    "triggered_rule": "Very Important Alert!"
  },
  "installation": { "uuid": "a8e5d2..." }
}

テンプレートを書く前に知っておくべきことが 4 つあります。

  • 有用な値はすべて data.event の下にあります。 {{title}} は何も描画しません。{{data.event.title}} がエラーです。
  • data.event.project は数値 ID で、スラッグではありません。 通知に読めるプロジェクト名を出したいなら、タイトルテンプレートに直接文字列で書き、プロジェクトごとにチャンネルを分けてください。
  • environment フィールドはありません。 環境は data.event.tags の中に ["environment", "production"] のペアとして届き、配列内の位置は安定しません。インデックスで取らないでください。環境の絞り込みは Sentry 側のルールで行います(ステップ 5)。
  • data.triggered_rule はルール名です。1 つのチャンネルで複数ルールを扱うとき、本文に入れると役立ちます。

イシューアラートの Sentry-Hook-Resource ヘッダーは event_alert です。Echobell のチャンネル条件でこれを必須にすれば、他のものはこのチャンネルを鳴らせません。

header["sentry-hook-resource"] == "event_alert"

ステップ 5 — 電話に値するものまで絞り込む

新規イシューのたびに鳴る着信チャンネルは、チャンネルが無いより悪いです。1 週間でミュートし、そして肝心のときにも鳴らなくなります。絞り込みは 2 か所で行います。

Sentry 側 はルールの conditions と filters で。

目的ルール設定
本番だけルールの Environment を production に
本当の障害だけフィルタ:The event's level equals fatal(または error)
一過性のブレを無視条件:The issue is seen more than 25 times in 1 hour
重要な経路だけフィルタ:The event's tags match transaction contains /checkout
再発だけ条件:A resolved issue changes state from resolved to unresolved

Echobell 側 では、Sentry で表現できないものや、今日は変更をマージできない場合の保険としてチャンネル条件を使います。

data.event.level == "fatal" || data.event.level == "error"

深刻度の絞り込みは、通常 Sentry のルール側が有利です。スロットルも同じ場所にあるからです。Echobell 側が有利なのは、1 つの Sentry ルールから 2 段階の緊急度を作りたいときです。

ステップ 6 — 警告には静かな入口を用意する

段階分けの目的は、電話がその重みを保つことです。時間指定(Time Sensitive) タイプの 2 つ目の Echobell チャンネルを作り、しきい値の低い 2 つ目の Sentry ルールを追加して、2 つ目の内部インテグレーションに向けます(1 インテグレーションにつき Webhook URL は 1 つなので、チャンネルを増やすならインテグレーションも増やします)。

現実の 1 週間に耐える構成はだいたいこうなります。

Sentry ルールレベル / しきい値Echobell チャンネル挙動
prod-fatalfatal、本番着信集中モードを突破して鳴る
prod-error-spikeerror、1 時間に 100 回超時間指定ロック画面に出るが鳴らない
new-issue-digest任意の新規イシュー通常普通のプッシュ、後で読む

業務時間外だけ鳴らす

日中はどうせ Sentry を見ています。Echobell は条件で使える UTC のシステム時刻変数を提供しているので、Sentry 側にルールを増やさずに時間帯で挙動を変えられます。

data.event.level == "fatal" && (hour >= 17 || hour < 9)

週末が本当に休みなら曜日判定も足します。

data.event.level == "fatal" && (hour >= 17 || hour < 9 || dayOfWeek == 0 || dayOfWeek == 6)

いずれも UTC なので、数値を決める前に自分のタイムゾーンから換算してください。変数の全一覧は条件リファレンスにあります。

2 つの落とし穴

落とし穴 1:Webhooks のチェックボックスを入れてしまう。 内部インテグレーションには独立した 2 つの Webhook 経路があります。Alert Rule Action トグルは、そのインテグレーションをアラートルールで選べるようにするもの——欲しいのはこちらです。一方 Webhooks のチェックボックス(issue、error、comment)は、そのリソースの すべての イベントを購読します。組織全体で、イシューが作成・解決・アサイン・アーカイブ・無視されるたびに、です。issue にチェックを入れると、同僚が何かを解決するたびに電話が鳴ります。すべて外しておけば、あなたのアラートルールだけが Webhook を発火します。

落とし穴 2:旧 Webhooks プラグインを使う。 プロジェクト単位の古い Legacy Integrations → WebHooks プラグインはまだ存在し、まだ動きます。インテグレーションを作らずに URL を貼るだけなので近道に見えます。しかしペイロードの形が違って(よりフラットで)、リクエストは署名されず、Sentry 自身も新規構成を別へ誘導しています。これを使うなら、上記とは別の変数パスが必要になります。内部インテグレーションを使ってください。

ペイロードサイズ、エラーの嵐、切り詰め

規模が出る前に知っておく価値のある制限が 3 つあります。

  • ボディ 1 MiB。 Echobell は 1 MiB を超えるトリガーボディを HTTP 413 で拒否します。Sentry のペイロードは完全なスタックトレースとリクエストコンテキストを運ぶため通常は数十 KB ですが、巨大なリクエストボディを持つイベントは上限に近づき得ます。Sentry 側に max_alerts のようなつまみはないので、対策は SDK の beforeSend で大きなリクエストボディを落とすこと。プライバシー上もそうすべき処理です。
  • トークンあたり毎分 120 リクエスト。 それを超えるとトリガーは 429 と RATE_LIMIT_EXCEEDED、Retry-After を返します。ここに収めてくれるのが Sentry の action interval で、30 minutes なら十分です。
  • 通知本文 1500 バイト。 これを超えた描画結果は端末に届く前に切り詰められます。data.event.title と culprit なら余裕ですが、data.event.exception を丸ごと流し込むのは無理ですし、ロック画面では読めません。詳細はリンクテンプレートの向こう側に置きましょう。

チームで共有する

Echobell のチャンネルは複数人が購読でき、購読者ごとに通知タイプを選べます。つまり同じ Sentry ルールで、オンコール担当の電話は鳴らし、他のメンバーには通常のプッシュとして届けられます。人数課金もローテーション設定もありません。

ただしこれはエスカレーションポリシーではありません。「5 分以内に誰も応答しなければ次の人に電話する」はありません。それが必要なら本物のオンコールプラットフォームが要ります。Echobell はその下の配信層を担います。

この構成で得られないもの

はっきり書いておきます。

  • 確認(ack)はありません。 電話に出ても Sentry には何も伝わらず、他の購読者の端末も止まりません。
  • ローテーションもエスカレーションもありません。 購読者全員に届くか、誰にも届かないかです。
  • Sentry を超える重複排除はありません。 グルーピングとスロットルは Sentry のルール側で、Echobell は届いたものを配信します。
  • 双方向同期はありません。 Sentry でイシューを解決しても、端末側は何も変わりません。

これらが致命的なら、これは適切な道具ではありません。必要なのが「決済が壊れたら起こしてくれ」なら、それを確実に得る最も安い方法のひとつです。

トラブルシューティング

ルールは発火するのに何も届かない。 インテグレーションの Alert Rule Action がオンか確認してください。オフだとそもそもルールのアクション一覧に出てきません。オンにする前に保存したルールは、古いアクションを保持し続けます。

通知は届くが中身が空。 テンプレートがトップレベルのキーを読んでいます。Sentry はすべてを data.event の下にネストします。

想定外のもので鳴る。 まずインテグレーションの Webhooks チェックボックス(落とし穴 1)、次にルールの environment が「All Environments」のままでないかを確認してください。

同じエラーで何度も鳴る。 Sentry ルールの action interval を上げてください。Echobell の再試行は別物です。設定の 失敗した通話を再試行 は、取り逃した着信をかけ直す機能です。

テストすら届かない。 まず curl でチャンネルを叩き、Echobell 側を切り分けます。

curl -X POST https://hook.echobell.one/t/<channel-token> \
  -H 'Content-Type: application/json' \
  -d '{"data":{"event":{"level":"fatal","title":"Test error","culprit":"manual test","metadata":{"type":"TestError"}}}}'

これで鳴るのに Sentry で鳴らないなら、問題はチャンネルではなくインテグレーションにあります。

よくある質問

Sentry はネイティブに電話をかけられますか?

いいえ。Sentry のイシューアラートのアクションは通知、チケット作成、ページング製品との連携です。音声通話には外部サービスが必要で、PagerDuty のようなページングプラットフォームか、Echobell のような「鳴る Webhook 受け口」を使います。

Sentry のアラートはおやすみモードを突破しますか?

着信型アラートとして届いた場合だけです。着信タイプの Echobell チャンネルは着信として振る舞うため、iOS の集中モードやおやすみモードが通します。どのアプリであれ通常のプッシュは通りません。

Webhook に Sentry の有料プランは必要ですか?

内部インテグレーションとアラートルールのアクションは、Sentry の Developer プラン以上で利用できます。Webhook 自体に追加費用はありません。

テンプレート変数が空になるのはなぜ?

ほぼ必ずパスが短すぎるからです。アラートのペイロードはイベントを data.event の下にネストするので、{{title}} ではなく {{data.event.title}} です。一度チャンネルを発火させ、アプリに記録されたリクエストボディで実際の形を確認してください。

特定の環境だけで通知するには?

Sentry のアラートルールの Environment フィールドを設定します。data.event.tags から読もうとしないでください。順序が保証されない [キー, 値] のペア配列です。

同じエラーで 2 人に電話できますか?

できます。チャンネルを共有し、各自が好みの通知タイプで購読します。確認機能がないので、「着信」を選んだ全員に電話がかかります。

絞り込みは Sentry と Echobell のどちらで?

可能なら Sentry で。ルールにはスロットルと環境スコープも同居します。1 つのルールから 2 段階の緊急度を作りたいとき、時間帯の窓が必要なとき、今日はルールを変更できないときは Echobell で。

まとめ

構成要素は 4 つです。着信チャンネル、Alert Rule Action がオンで Webhooks のチェックが外れた内部インテグレーション、電話に値するほど絞り込まれたアラートルール、そして data.event を読むテンプレート。このページの残りはすべて、1 か月後もその着信音が意味を保てるよう、十分に狭く保つための話です。

iPhone 版 Echobell をダウンロード、または Google Play で入手。本当に大事なものをこの経路に預ける前に、まず上の curl を送ってください。

関連記事