---
title: "Sentry 电话告警：只为压垮生产的错误响铃"
description: "Sentry 没有打电话这个动作。用 Webhook 把 issue 告警变成真正响铃的来电：集成配置、真实载荷结构、以及该怎么过滤。"
date: 2026-09-25
author: Nooc
authorAvatarLink: /images/avatars/nooc.webp
authorLink: https://nooc.me
tags:
  - Sentry
  - 错误监控
  - 电话告警
  - Webhook
  - 告警疲劳
  - on-call
---

# Sentry 电话告警：只为压垮生产的错误响铃

Sentry 不能给你打电话。它能发邮件、发 Slack、把告警交给 PagerDuty——但没有内置的语音动作。想在不买一整套事件管理平台的前提下拿到这个能力，办法是把 Sentry 的 issue 告警发给一个会响铃的 Webhook：建一个 internal integration，指向 Echobell 的来电频道，再在前面加一层过滤，只让真正压垮生产的错误通过。

这篇文章讲完整链路：集成、告警规则、Sentry 真正发出的载荷、读取它的模板，以及让大多数人半途放弃的两个坑。

## 为什么 Sentry 自己打不了电话

Sentry 的 issue 告警动作分三类：通知（邮件、Slack、Discord、Microsoft Teams）、建工单（Jira、GitHub、Azure DevOps）、交给寻呼产品（PagerDuty、Opsgenie）。每一类的终点要么是一块你得正好在看的屏幕，要么是另一个平台上按人头收费的席位。

下午两点这没问题。凌晨三点，一条 Slack 消息和静音没有区别，推送通知也打不过勿扰模式。对于那一小撮「晚两小时就要真金白银亏钱」的错误——结账接口全返 500、登录全被拒、后台任务在悄悄丢单——你需要的是一台会响的设备。

Webhook 就是这个接口。Sentry 可以把任意 HTTPS 端点作为告警规则的动作；[Echobell](https://apps.apple.com/app/apple-store/id6743597198?pt=128151925&ct=blog-sentry-phone-call-alerts-zh&mt=8) 则把这个 HTTP 请求变成能穿透 iOS 专注模式的来电式提醒。

## 你需要什么

- 一个你能进到 **Settings → Developer Settings** 的 Sentry 组织（owner 或 manager 权限）
- 装好 Echobell（[App Store](https://apps.apple.com/app/apple-store/id6743597198?pt=128151925&ct=blog-sentry-phone-call-alerts-zh&mt=8) / [Google Play](https://play.google.com/store/apps/details?id=one.echobell.echobellandroid)）
- 五分钟

你这边不需要任何公网可达的东西。请求是 Sentry 主动发出的，你只负责接收。

## 第 1 步 —— 建一个会响铃的频道

在 Echobell 里新建频道，通知类型选 **来电（Calling）**。这正是整件事的意义所在：来电频道的行为像一通打进来的电话而不是推送，所以它能穿过专注模式和勿扰模式。

模板要写成凌晨三点也能看懂的样子。Sentry 的载荷嵌套很深，所以变量路径比平时长：

```
标题：{{data.event.level}}: {{data.event.metadata.type}}
正文：{{data.event.title}} — {{data.event.culprit}}
```

在高级设置里配好**链接模板**，这样点通知就能直接打开这个 issue：

```
{{data.event.web_url}}
```

从频道详情页复制 Webhook URL，形如：

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

## 第 2 步 —— 创建 Sentry internal integration

Sentry 只有通过 *integration* 才把 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. 保存

internal integration 只在你自己的组织内可用，并且会自动安装。这套配置用不到它生成的 token。

## 第 3 步 —— 把集成挂到告警规则上

进入 **Alerts → Create Alert → Issue Alert**，或者编辑已有规则。

在 **Then perform these actions** 里添加 **Send a notification via an integration**，选 **Echobell**。

把 **Action interval**（「同一条告警重复触发时」的节流）至少设成 `30 minutes`。默认是每次触发都发，一个每分钟报 400 次的错误会一直打你电话，直到你把它关掉为止。

保存，然后用规则的测试/预览功能跑一次，先看到真实载荷到达，再去信任它。

## 第 4 步 —— 搞清楚到底收到了什么

大多数配置死在这一步，因为载荷的形状和你猜的不一样。Sentry 把所有东西都包了一层：

```json
{
  "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..." }
}
```

动手写模板之前，有四件事值得先知道：

- **有用的东西全在 `data.event` 底下。** `{{title}}` 什么都渲染不出来；`{{data.event.title}}` 才是那条错误。
- **`data.event.project` 是数字 ID，不是 slug。** 想让通知里出现可读的项目名，就把它当字面量写进频道标题模板，并且一个项目配一个频道。
- **没有 `environment` 字段。** 环境是以 `["environment", "production"]` 这样的键值对出现在 `data.event.tags` 里的，而且它在数组里的位置不固定——所以别按下标取。环境过滤放到 Sentry 规则里做（第 5 步）。
- **`data.triggered_rule`** 是规则名。当一个频道服务多条规则时，把它放进正文里很有用。

issue 告警的 `Sentry-Hook-Resource` 请求头是 `event_alert`。你可以在 Echobell 频道条件里要求它，这样别的东西就没法触发这个频道：

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

## 第 5 步 —— 过滤到「值得一通电话」的程度

一个每来新 issue 就响铃的来电频道，比没有这个频道还糟：一周之内你就会把它静音，然后真正要紧的那次它也不会响。过滤要放在两个地方。

**在 Sentry 里**，用规则自带的 conditions 和 filters：

| 目标 | 规则配置 |
| --- | --- |
| 只要生产环境 | 把规则的 **Environment** 设为 `production` |
| 只要真的坏了 | Filter：`The event's level equals fatal`（或 `error`） |
| 别为偶发抖动响 | Condition：`The issue is seen more than 25 times in 1 hour` |
| 只盯关键路径 | Filter：`The event's tags match transaction contains /checkout` |
| 只要回归 | Condition：`A resolved issue changes state from resolved to unresolved` |

**在 Echobell 里**，把频道条件当兜底，用来表达 Sentry 表达不了的东西，或者用来应付今天改不动的规则：

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

严重级别一般还是在 Sentry 规则里筛更好，因为节流也在那儿。放在 Echobell 里更好的场景是：你想用一条 Sentry 规则喂出两档紧急程度。

## 第 6 步 —— 给警告级别留一扇安静的门

分级的意义在于让「电话」保住它的分量。再建一个 **时效性（Time Sensitive）** 类型的 Echobell 频道，再加一条门槛更低的 Sentry 规则，指向第二个 internal integration（一个集成只存一个 Webhook URL，所以第二个频道需要第二个集成）。

一套能扛过真实一周的配置大致长这样：

| Sentry 规则 | 级别 / 阈值 | Echobell 频道 | 行为 |
| --- | --- | --- | --- |
| `prod-fatal` | `fatal`，生产 | 来电 | 穿透专注模式响铃 |
| `prod-error-spike` | `error`，1 小时超 100 次 | 时效性 | 直接上锁屏，不响铃 |
| `new-issue-digest` | 任意新 issue | 普通 | 普通推送，有空再看 |

## 只在下班时间响

白天你本来就盯着 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，定死数字之前先从你自己的时区换算一遍。完整变量表见[条件参考](/zh/docs/conditions)。

## 两个坑

**坑 1：勾了 Webhooks 那几个框。** internal integration 有两条互相独立的 Webhook 路径。**Alert Rule Action** 开关让这个集成能在告警规则里被选中——这是你要的那条。而 **Webhooks** 复选框（`issue`、`error`、`comment`）订阅的是该资源的**每一个**事件：整个组织范围内每次 issue 被创建、解决、指派、归档、忽略。勾上 `issue`，同事解决一个问题你的电话就会响。全都不勾，就只有你的告警规则会触发 Webhook。

**坑 2：用了旧版 Webhooks 插件。** 老的按项目配置的 **Legacy Integrations → WebHooks** 插件还在，也还能用，而且看起来像条捷径——不用建集成就能粘个 URL。但它的载荷结构不一样（更扁平），请求没有签名，Sentry 自己也在把新用户往别处引。如果你用它，上面那些变量路径全都得换。请用 internal integration。

## 载荷大小、错误风暴与截断

规模上来之前，有三个限制值得先知道：

- **1 MiB 请求体。** 超过 1 MiB 的触发请求会被 Echobell 以 HTTP 413 拒绝。Sentry 的载荷带着完整堆栈和请求上下文，通常落在几十 KB，但一个带巨大请求体的事件可能逼近上限。Sentry 那边没有 `max_alerts` 这种旋钮，所以缓解办法是在 SDK 的 `beforeSend` 里清洗大的请求体——这件事你出于隐私考虑本来也该做。
- **每个 token 每分钟 120 次请求。** 超过之后触发接口会返回 `429`、`RATE_LIMIT_EXCEEDED` 和一个 `Retry-After`。让你待在线内的正是 Sentry 的 action interval，`30 minutes` 绰绰有余。
- **通知正文 1500 字节。** 更长的渲染结果在到达设备前会被截断。`data.event.title` 加 `culprit` 很宽裕；把 `data.event.exception` 整个倒进去则不行，而且在锁屏上根本读不了。细节留给链接模板。

## 和团队共享

一个 Echobell 频道可以被多人订阅，每个订阅者各自选自己的通知类型。所以同一条 Sentry 规则可以让值班工程师的手机响铃，同时对其他人只是一条普通推送——没有按人头计费，也没有排班配置。

它不是升级策略。这里没有「五分钟没人确认就打给下一个人」。真需要那个，你需要一个真正的 on-call 平台；Echobell 覆盖的是它下面那层投递。

## 这套方案给不了你什么

不如把话说明白：

- **没有确认（ack）。** 接起电话不会告诉 Sentry 任何事，也不会让其他订阅者的手机停下来。
- **没有排班和升级。** 要么订阅的人都收到，要么都收不到。
- **除 Sentry 之外没有额外去重。** 分组和节流都发生在 Sentry 规则里，Echobell 只投递到达的东西。
- **没有双向同步。** 在 Sentry 里解决问题不会清掉你手机上的任何东西。

如果这些是硬伤，那这不是对的工具。如果你真正需要的是「结账坏了就把我叫醒」，它大概是拿到这个结果最便宜的可靠方式。

## 排障

**规则触发了但什么都没到。** 检查集成里的 **Alert Rule Action** 是不是开着。如果是关的，这个集成根本不会出现在规则的动作列表里——而在你打开它之前保存过的规则，会保留一个失效的动作。

**收到通知但内容是空的。** 你的模板在读顶层键。Sentry 把所有东西都嵌在 `data.event` 下面。

**响了些你没预料到的东西。** 先查集成上的 Webhooks 复选框（坑 1），再看规则的 environment 是不是留在了 "All Environments"。

**同一个错误反复响。** 调高 Sentry 规则的 action interval。Echobell 的重试是另一回事——应用设置里的 **重试失败来电** 是在你漏接时重拨，不是一回事。

**连测试都收不到。** 先用 `curl` 触发一次频道，排除 Echobell 这一侧：

```bash
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 的 issue 告警动作只有通知、建工单，以及和寻呼类产品的集成。语音电话需要外部服务——要么是 PagerDuty 这类寻呼平台，要么是一个会响铃的 Webhook 接收端，比如 Echobell。

### Sentry 告警能穿透勿扰模式吗？

只有当它以来电式提醒到达时才能。来电类型的 Echobell 频道行为像一通打进来的电话，iOS 的专注模式和勿扰模式会放行。任何应用的普通推送都不行。

### 用 Webhook 需要付费的 Sentry 套餐吗？

internal integration 和告警规则动作在 Sentry 的 Developer 套餐及以上都可用。Webhook 本身不额外收费。

### 为什么我的 Sentry 模板变量是空的？

几乎总是因为路径太短。告警载荷把事件嵌在 `data.event` 下，所以要写 `{{data.event.title}}` 而不是 `{{title}}`。触发一次频道，在应用里查看记录下来的请求体，就能看到你实际收到的结构。

### 怎么只对某一个环境告警？

设置 Sentry 告警规则上的 **Environment** 字段。别试图从 `data.event.tags` 里读——那是一个 `[键, 值]` 对数组，顺序没有保证。

### 同一个错误能同时打给两个人吗？

可以。共享频道，让每个人用自己想要的通知类型订阅。因为没有确认机制，所以选了「来电」的人都会被打电话。

### 过滤应该放在 Sentry 还是 Echobell？

能放 Sentry 就放 Sentry——节流和环境范围也都在规则里。放 Echobell 更合适的情况是：你想从一条规则里分出两档紧急程度、你需要一个按时段的窗口、或者你今天改不动那条规则。

## 小结

整套东西就四件：一个来电频道、一个打开了 **Alert Rule Action** 且 Webhooks 复选框全不勾的 internal integration、一条窄到配得上一通电话的告警规则，以及会读 `data.event` 的模板。这一页上剩下的所有内容，都是为了让它一个月之后还足够窄，让那声铃响仍然有意义。

[在 iPhone 上下载 Echobell](https://apps.apple.com/app/apple-store/id6743597198?pt=128151925&ct=blog-sentry-phone-call-alerts-zh&mt=8) 或[在 Google Play 获取](https://play.google.com/store/apps/details?id=one.echobell.echobellandroid)，然后先把上面那条 `curl` 发一次，再把任何真正重要的事情托付给这条链路。

## 相关内容

- [频道条件参考](/zh/docs/conditions)
- [Webhook 集成指南](/zh/docs/webhook)
- [开发者的告警疲劳修复指南](/zh/blog/fix-alert-fatigue-developer-guide)
- [Prometheus Alertmanager 电话告警](/zh/blog/alertmanager-phone-call-alerts)
- [让关键告警穿透 iOS 专注模式](/zh/blog/how-to-bypass-ios-focus-mode-for-critical-alerts)
