集成指南
了解如何将告警源接入 AlertHub,实现统一的告警通知管理。
接口地址
httpPOST /api/receive/webhook/{projectSlug}将 {projectSlug} 替换为你在设置页面创建的项目标识符。
请求头
| Header | 值 | 说明 |
|---|---|---|
| Content-Type | application/json | 必填 |
| X-API-Key | 你的项目 API 密钥 | 必填,在项目设置中获取 |
请求体字段
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
| title | string | 是 | 告警标题 |
| description | string | 否 | 告警详细描述 |
| severity | string | 否 | 严重级别:critical warning info,默认 warning |
| channels | string[] | 否 | 目标通知渠道的 ID 列表(NotificationChannel UUID)。 AlertHub 会投递到其中每个已启用的渠道。 为空或全部无效时,告警仍会记录,但不发送任何通知。 渠道 ID 通过下方「列出可用渠道」接口获取。 |
| labels | object | 否 | 自定义标签,展示在告警详情中 |
| contentType | string | 否 | 内容类型:text image html markdown, 默认 text(详见下方「富内容告警」) |
| imageUrl | string | 否 | 图片地址,contentType=image 时必填, 缺失会自动降级为 text |
| htmlBody | string | 否 | HTML 内容,contentType=html 时必填 |
| markdownBody | string | 否 | Markdown 内容,contentType=markdown 时必填 |
| clickUrl | string | 否 | 点击跳转的 HTTP/HTTPS URL,渲染为通知里的「查看详情」链接 |
| clickSchema | string | 否 | 自定义 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 还可以携带图片、HTML、Markdown三种内容形态, 以及点击跳转(HTTP URL 或自定义 URI scheme)。 每条告警的 contentType 互斥 —— 一次只能是其中一种。
contentType 取值
| contentType | 配套必填字段 | 说明 |
|---|---|---|
| text(默认) | — | 纯文本 title + description;现有渠道按各自模板渲染 |
| image | imageUrl | 通知里嵌入图片;不支持嵌入的渠道(飞书/短信)退化为文本链接 |
| html | htmlBody | 仅邮件渠道原生渲染;其他渠道 strip 成纯文本 |
| markdown | markdownBody | 企微/钉钉/飞书原生渲染;邮件经 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/123、dingtalk://...)。 邮件和飞书会作为「在 App 中打开」备选链接展示
浏览器推送(Web Push)的点击行为是个例外:通知点击会**先跳转到 AlertHub 自己的告警详情页**/alerts/{id}, 而不是直接打开 clickUrl/clickSchema。详情页能看到完整 label / payload / 通知决策链,你传的两个链接在那里作为「查看详情」和「在 App 中打开」按钮展示。
校验:clickUrl 必须以 http(s):// 开头;clickSchema 必须含 :// 且不能是 http(s)。 不符合的会被丢弃并 warn。
各渠道渲染对照
| 渠道 | image | html | markdown | click |
|---|---|---|---|---|
| 企业微信 | 嵌入 markdown 图片 | strip 成文本 | 原生渲染 | url ✓ / schema ✗ |
| 钉钉 | 嵌入 markdown 图片 | strip 成文本 | 原生渲染 | url ✓ / schema ✗ |
| 飞书 | 退化为文本链接 * | strip 成文本 | lark_md 原生渲染 | url ✓ / schema ✓ |
| 邮件 | <img> 嵌入 | 原生渲染(不 sanitize) | marked 转 HTML | url ✓ / schema ✓ |
| 自定义 Webhook | JSON 透传 | JSON 透传 | JSON 透传 | url ✓ / schema ✓ |
| 阿里云短信 | 忽略 | 忽略 | 忽略 | url 进 templateParam ** |
| 浏览器推送 | notification.image 大图 | strip 成文本 body | strip 成文本 body | 先跳 AlertHub 详情页 *** |
* 飞书原生 image 元素需要先调用图床 API 上传拿 image_key,v1 暂不做此流程, 直接渲染成文本链接。
** SMS 受阿里云短信模板限制:只有当短信模板里预留了${url}变量,clickUrl 才会被填入。
*** 浏览器推送点击通知会进入 AlertHub 的告警详情页/alerts/{id}, 在那里能看到完整 label / payload / 通知决策链, 你传的 clickUrl / clickSchema 在该页面作为二级按钮展示。
心跳监控用于检测服务是否存活。你的服务定期向 AlertHub 发送心跳请求(Ping),如果在预期时间内未收到心跳,AlertHub 将自动触发告警。当服务恢复心跳后,告警会自动恢复。
使用流程
- 在 设置 页面的项目中启用心跳监控,使用项目标识符作为 slug
- 在你的服务中添加定时任务,周期性调用 Ping 接口
- 如果超过「检测间隔 + 宽限期」未收到心跳,将自动触发 Critical 级别告警
- 恢复心跳后告警自动解除
Ping 接口
httpPOST /api/heartbeat/{slug}/ping此接口无需认证,可直接调用。将 {slug} 替换为项目的标识符。
参数说明
| 参数 | 默认值 | 说明 |
|---|---|---|
| interval | 60 秒 | 预期心跳发送间隔,你的服务应按此频率调用 Ping |
| gracePeriod | 30 秒 | 超过 interval 后额外的宽限时间,超过后触发告警 |
例如 interval=60, gracePeriod=30,则超过 90 秒未收到心跳将触发告警。
cURL 示例
bash# 每 60 秒发送一次心跳
curl -X POST https://alert.model1235.com/api/heartbeat/my-service/pingCrontab 示例
bash# 每分钟发送心跳
* * * * * curl -fsS -o /dev/null https://alert.model1235.com/api/heartbeat/my-service/pingNode.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