AlertHub

集成指南

了解如何将告警源接入 AlertHub,实现统一的告警通知管理。

接入 AlertHub 只需要两个东西:

  1. 项目标识符(slug):URL 路径里用,类似POST /api/receive/webhook/{slug}
  2. 项目 API 密钥(apiKey):作为X-API-Key 头传入,用来鉴权

这两个值由运维侧在 设置 页面创建项目时生成,问运维要即可。下方按告警源类型给出接入示例。

通知渠道由运维侧创建并持有密钥;客户端在发告警时通过 channels 字段直接指定要发往的渠道 ID(见下方「列出可用渠道」)。详见 运维手册

接口地址

httpPOST /api/receive/webhook/{projectSlug}

{projectSlug} 替换为你在设置页面创建的项目标识符。

请求头

Header说明
Content-Typeapplication/json必填
X-API-Key你的项目 API 密钥必填,在项目设置中获取

请求体字段

字段类型必填说明
titlestring告警标题
descriptionstring告警详细描述
severitystring严重级别:critical warning info,默认 warning
channelsstring[]目标通知渠道的 ID 列表(NotificationChannel UUID)。 AlertHub 会投递到其中每个已启用的渠道。 为空或全部无效时,告警仍会记录,但不发送任何通知。 渠道 ID 通过下方「列出可用渠道」接口获取。
labelsobject自定义标签,展示在告警详情中
contentTypestring内容类型:text image html markdown, 默认 text(详见下方「富内容告警」)
imageUrlstring图片地址,contentType=image 时必填, 缺失会自动降级为 text
htmlBodystringHTML 内容,contentType=html 时必填
markdownBodystringMarkdown 内容,contentType=markdown 时必填
clickUrlstring点击跳转的 HTTP/HTTPS URL,渲染为通知里的「查看详情」链接
clickSchemastring自定义 URI scheme(如 myapp://alert/123), 用于深链接到原生 App

cURL 示例

bashcurl -X POST https://alert.model1235.com/api/receive/webhook/my-app \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-api-key" \
  -d '{
    "title": "数据库连接异常",
    "description": "MySQL 主库连接数超过阈值 (当前: 450/500)",
    "severity": "critical",
    "channels": ["3f9a...uuid-1", "7c2b...uuid-2"],
    "labels": {
      "env": "production",
      "service": "user-api",
      "instance": "db-master-01"
    }
  }'

Python 示例

pythonimport requests

requests.post(
    "https://alert.model1235.com/api/receive/webhook/my-app",
    headers={"X-API-Key": "your-api-key"},
    json={
        "title": "磁盘使用率过高",
        "description": "/data 分区使用率 92%",
        "severity": "warning",
        "channels": ["3f9a...uuid-1"],
        "labels": {"host": "web-server-03"}
    }
)

路由由客户端决定:你在发告警时通过channels 字段 填入目标渠道的 ID(UUID),AlertHub 投递到这些渠道。 渠道由运维侧创建并持有所有密钥;客户端只能拿到脱敏后的渠道目录 (id /name /type),用来把人类可读的名称映射到 ID。

接口

httpGET /api/receive/channels/{projectSlug}

使用项目 X-API-Key 头鉴权。 只返回已启用的渠道,不含任何配置或密钥。

cURL 示例

bashcurl https://alert.model1235.com/api/receive/channels/my-app \
  -H "X-API-Key: your-api-key"
# → [
#     { "id": "3f9a...uuid-1", "name": "运维企微", "type": "wecom" },
#     { "id": "7c2b...uuid-2", "name": "值班邮件", "type": "email" }
#   ]

按级别选不同渠道:AlertHub 不做服务端级别路由。 「critical 发企微+短信、warning 只发企微」这类逻辑由你的程序自己决定—— 按你的判断把对应渠道的 ID 放进 channels 即可。

除了纯文本,webhook payload 还可以携带图片HTMLMarkdown三种内容形态, 以及点击跳转(HTTP URL 或自定义 URI scheme)。 每条告警的 contentType 互斥 —— 一次只能是其中一种。

contentType 取值

contentType配套必填字段说明
text(默认)纯文本 title + description;现有渠道按各自模板渲染
imageimageUrl通知里嵌入图片;不支持嵌入的渠道(飞书/短信)退化为文本链接
htmlhtmlBody仅邮件渠道原生渲染;其他渠道 strip 成纯文本
markdownmarkdownBody企微/钉钉/飞书原生渲染;邮件经 marked 转 HTML;浏览器推送 strip 成纯文本

软校验:如果 contentType 设了但对应的 body 字段缺失(例如 image 但没 imageUrl), AlertHub 会自动降级为 text 并打 warn 日志,不会拒绝告警

图片告警示例

bashcurl -X POST https://alert.model1235.com/api/receive/webhook/my-app \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-api-key" \
  -d '{
    "title": "监控大盘异常",
    "severity": "critical",
    "contentType": "image",
    "imageUrl": "https://grafana.example.com/render/d-solo/abc/cpu?width=800&height=400",
    "clickUrl": "https://grafana.example.com/d/abc/cpu-dashboard",
    "labels": { "service": "user-api" }
  }'

Markdown 告警示例

bashcurl -X POST https://alert.model1235.com/api/receive/webhook/my-app \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-api-key" \
  -d '{
    "title": "发布失败",
    "severity": "critical",
    "contentType": "markdown",
    "markdownBody": "## 发布失败\n\n**服务**: user-api  \n**版本**: v2.3.1\n\n### 错误堆栈\n\n```\nERROR: connect ECONNREFUSED 10.0.0.5:5432\n```\n\n- [ ] 检查数据库连通性\n- [ ] 回滚到 v2.3.0",
    "clickUrl": "https://ci.example.com/jobs/12345",
    "labels": { "env": "production", "service": "user-api" }
  }'

HTML 告警示例

bashcurl -X POST https://alert.model1235.com/api/receive/webhook/my-app \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-api-key" \
  -d '{
    "title": "周报",
    "severity": "info",
    "contentType": "html",
    "htmlBody": "<h2>本周告警统计</h2><table><tr><th>项目</th><th>触发数</th></tr><tr><td>user-api</td><td>12</td></tr></table>",
    "labels": { "type": "report" }
  }'

⚠️ HTML 不做服务端 sanitize。只在受信任的内部调用方使用, 不要把外部不可信来源的 HTML 直接转发到这个接口。

点击跳转:clickUrl 和 clickSchema

两者可以同时设置,目的不同:

  • clickUrl:HTTP/HTTPS 链接,所有渠道都用,在通知里渲染成「查看详情」按钮/链接
  • clickSchema:自定义 URI scheme,主要用于深链接打开原生 App (如 myapp://alert/123dingtalk://...)。 邮件和飞书会作为「在 App 中打开」备选链接展示

浏览器推送(Web Push)的点击行为是个例外:通知点击会**先跳转到 AlertHub 自己的告警详情页**/alerts/{id}, 而不是直接打开 clickUrl/clickSchema。详情页能看到完整 label / payload / 通知决策链,你传的两个链接在那里作为「查看详情」和「在 App 中打开」按钮展示。

校验:clickUrl 必须以 http(s):// 开头;clickSchema 必须含 :// 且不能是 http(s)。 不符合的会被丢弃并 warn。

各渠道渲染对照

渠道imagehtmlmarkdownclick
企业微信嵌入 markdown 图片strip 成文本原生渲染url ✓ / schema ✗
钉钉嵌入 markdown 图片strip 成文本原生渲染url ✓ / schema ✗
飞书退化为文本链接 *strip 成文本lark_md 原生渲染url ✓ / schema ✓
邮件<img> 嵌入原生渲染(不 sanitize)marked 转 HTMLurl ✓ / schema ✓
自定义 WebhookJSON 透传JSON 透传JSON 透传url ✓ / schema ✓
阿里云短信忽略忽略忽略url 进 templateParam **
浏览器推送notification.image 大图strip 成文本 bodystrip 成文本 body先跳 AlertHub 详情页 ***

* 飞书原生 image 元素需要先调用图床 API 上传拿 image_key,v1 暂不做此流程, 直接渲染成文本链接。
** SMS 受阿里云短信模板限制:只有当短信模板里预留了${url}变量,clickUrl 才会被填入。
*** 浏览器推送点击通知会进入 AlertHub 的告警详情页/alerts/{id}, 在那里能看到完整 label / payload / 通知决策链, 你传的 clickUrl / clickSchema 在该页面作为二级按钮展示。

心跳监控用于检测服务是否存活。你的服务定期向 AlertHub 发送心跳请求(Ping),如果在预期时间内未收到心跳,AlertHub 将自动触发告警。当服务恢复心跳后,告警会自动恢复。

使用流程

  1. 设置 页面的项目中启用心跳监控,使用项目标识符作为 slug
  2. 在你的服务中添加定时任务,周期性调用 Ping 接口
  3. 如果超过「检测间隔 + 宽限期」未收到心跳,将自动触发 Critical 级别告警
  4. 恢复心跳后告警自动解除

Ping 接口

httpPOST /api/heartbeat/{slug}/ping

此接口无需认证,可直接调用。将 {slug} 替换为项目的标识符。

参数说明

参数默认值说明
interval60 秒预期心跳发送间隔,你的服务应按此频率调用 Ping
gracePeriod30 秒超过 interval 后额外的宽限时间,超过后触发告警

例如 interval=60, gracePeriod=30,则超过 90 秒未收到心跳将触发告警。

cURL 示例

bash# 每 60 秒发送一次心跳
curl -X POST https://alert.model1235.com/api/heartbeat/my-service/ping

Crontab 示例

bash# 每分钟发送心跳
* * * * * curl -fsS -o /dev/null https://alert.model1235.com/api/heartbeat/my-service/ping

Node.js 示例

javascript// 每 60 秒发送一次心跳
setInterval(() => {
  fetch("https://alert.model1235.com/api/heartbeat/my-service/ping", {
    method: "POST"
  }).catch(err => console.error("heartbeat ping failed:", err));
}, 60_000);

Python 示例

pythonimport threading, requests

def heartbeat():
    threading.Timer(60, heartbeat).start()
    try:
        requests.post("https://alert.model1235.com/api/heartbeat/my-service/ping")
    except Exception as e:
        print(f"heartbeat ping failed: {e}")

heartbeat()

Docker 健康检查

dockerfileHEALTHCHECK --interval=60s --timeout=5s --retries=1 \
  CMD curl -fsS -o /dev/null https://alert.model1235.com/api/heartbeat/my-service/ping