故障Webhook推送¶
Webhook 用于将故障的关键变化推送到第三方系统。您可以将故障信息接入 ITSM、自动化平台或内部业务系统,由第三方根据收到的数据继续创建工单、同步状态或触发自动化流程。
在故障中心 > 配置管理 > Webhook中可创建和管理 Webhook。
使用前提¶
请先准备一个能够接收 POST 请求的第三方地址。Webhook 支持 HTTP 和 HTTPS 地址;建议在生产环境使用 HTTPS。出于安全考虑,不能配置内网、回环或本机地址。
配置 Webhook¶
- 在Webhook页面点击新建 Webhook;
- 填写名称和第三方接收 URL;
- 选择需要推送的故障事件: | 事件 | 说明 | | --- | --- | | 故障创建 | 新故障创建时推送。 | | 状态变化 | 故障状态发生变化时推送。 | | 等级变更 | 故障等级发生变化时推送。 | | 处理人变化 | 故障处理人发生变化时推送。 | | 聚合变更 | 聚合故障关联事件发生变化时推送。仅对通过故障聚合规则生成的故障生效。 |
- 点击生成 Token。Token 仅由 观测云 平台生成,请立即复制并安全保存到第三方接收服务的密钥配置中;
- 设置启用状态并保存。
注意
- Token 是 Webhook 的认证密钥。生成后平台仅展示一次明文,后续页面仅显示脱敏值;重新生成 Token 会使旧 Token 立即失效;
- Webhook 投递失败不会影响故障的创建、更新、恢复和关闭;
- Webhook 使用固定请求方式、鉴权方式和超时时间,无需额外配置。
测试发送¶
保存 Webhook 后,可在该 Webhook 的操作项中点击测试并选择事件类型。系统会按正式请求格式发送一条测试数据,Payload 中的 isTest 为 true。
测试结果可在发送历史中查看。测试发送不影响真实故障,也不会计入正式发送统计。
请求协议¶
系统以 POST 方式发送 JSON 数据,请求超时时间为 5 秒,不跟随重定向。接收服务返回任意 2xx 状态码即视为投递成功。
请求 Header¶
| Header | 说明 |
|---|---|
Content-Type |
固定为 application/json。 |
Authorization |
固定格式为 Bearer <Token>。 |
X-Incidents-Event-Id |
本次投递的唯一标识,可用于接收端幂等处理。 |
X-Incidents-Timestamp |
Unix 秒级时间戳。 |
X-Incidents-Signature |
HMAC-SHA256 签名,格式为 v1=<signature>。 |
Payload 示例¶
{
"eventId": "evt_01JXYZ...",
"eventTypes": [
"incident.created",
"incident.status_changed"
],
"eventTime": 1783000000,
"workspaceUUID": "wksp_xxx",
"incident": {
"uuid": "incident_xxx",
"name": "API service unavailable",
"level": "level_1",
"incidentsStatus": "working",
"eventRelations": [
{
"df_fault_id": "fault_xxx",
"status": "critical"
}
]
},
"isTest": false
}
字段说明:
| 字段 | 说明 |
|---|---|
eventId |
一次 Webhook 投递批次的唯一 ID,不是原始事件 ID。 |
eventTypes |
本次批次包含的故障变化类型。 |
eventTime |
本次投递的 Unix 秒级时间戳。 |
workspaceUUID |
故障所属工作空间 UUID。 |
incident |
故障当前完整快照,具体字段见下文。resourceContent 不会推送。 |
isTest |
是否为测试发送。 |
incident 字段说明¶
incident 为发送时刻的故障快照。它会随故障的等级、状态、处理人和关联事件变化而更新。
| 字段 | 类型 | 说明 |
|---|---|---|
id |
integer | 故障记录的内部数字 ID。 |
uuid |
string | 故障 UUID,格式为 incident_xxx,建议作为第三方故障主键。 |
workspaceUUID |
string | 故障所属工作空间 UUID。 |
name |
string | 故障标题。 |
level |
string | 当前故障等级 UUID 或等级标识。 |
description |
string | 故障描述。 |
incidentsStatus |
string | 故障业务状态。常见值包括 open(待分配)、working(处理中)、resolved(已解决)和 closed(已关闭)。 |
assigner |
array | 当前处理人账号 UUID 列表;未分配时为空数组。 |
statusTime |
object | 各故障业务状态对应的时间戳记录。 |
statusChangeTime |
integer | 最近一次故障业务状态变更的 Unix 秒级时间戳。 |
cumulativeTime |
integer | 故障累计持续时长,单位为秒。 |
eventCount |
integer | 当前故障累计关联的事件数量。 |
eventUpdateAt |
integer | 最近一次关联事件更新的 Unix 秒级时间戳。 |
eventRelations |
array | 当前关联事件快照,字段说明见下文。 |
source |
string | 故障来源标识。 |
resourceCategory |
string | 来源资源分类。 |
resourceType |
string | 来源资源类型。 |
resourceUUID |
string | 来源资源 UUID。 |
resourceUrl |
string | 来源资源的访问地址;无地址时为空。 |
resourceIdentity |
string | 用于识别同一来源故障的资源标识。 |
dimensionTag |
object | 故障关联的维度标签。 |
dtHost |
string | 提取出的主机维度值。 |
dtService |
string | 提取出的服务维度值。 |
dtResource |
string | 提取出的资源维度值。 |
dtPodName |
string | 提取出的 Pod 名称维度值。 |
dtAppName |
string | 提取出的应用名称维度值。 |
dtAppId |
string | 提取出的应用 ID 维度值。 |
dtEnv |
string | 提取出的环境维度值。 |
dtUrl |
string | 提取出的 URL 维度值。 |
extend |
object | 故障扩展信息。该对象会随产品能力演进扩展,第三方应忽略不识别的字段。 |
status |
integer | 故障记录状态,用于标识记录是否有效;不要将其作为故障业务状态使用。 |
creator |
string | 创建该故障记录的账号 UUID 或系统标识。 |
updator |
string | 最近更新该故障记录的账号 UUID 或系统标识。 |
createAt |
integer | 故障记录创建时间,Unix 秒级时间戳。 |
updateAt |
integer | 故障记录最近更新时间,Unix 秒级时间戳。 |
deleteAt |
integer | 故障记录删除时间,未删除时通常为负值。 |
eventRelations 中每一项表示当前关联的一个事件。稳定返回的字段包括:
| 字段 | 类型 | 说明 |
|---|---|---|
df_fault_id |
string | 关联事件的故障 ID。 |
status |
string | 关联事件当前状态,例如 critical 或 ok。 |
不同事件来源可携带额外字段。第三方应按 df_fault_id 识别关联事件,并忽略未识别的扩展字段。聚合故障的关联事件快照发生变化时,会触发"聚合变更"事件。
验证请求来源¶
第三方接收服务应同时校验 Token、时间戳和签名。
签名原文为:
其中 rawRequestBody 是接收到的原始请求字节,不应先格式化或重新序列化 JSON。推荐仅接受当前时间前后 5 分钟内的请求,并按 eventId 去重。
import hashlib
import hmac
import time
def verify_webhook(request, token):
raw_body = request.get_data(cache=True)
timestamp = request.headers.get("X-Incidents-Timestamp", "")
event_id = request.headers.get("X-Incidents-Event-Id", "")
signature = request.headers.get("X-Incidents-Signature", "")
authorization = request.headers.get("Authorization", "")
if not hmac.compare_digest(authorization, f"Bearer {token}"):
return False
if not timestamp.isdigit() or abs(time.time() - int(timestamp)) > 300:
return False
message = f"{timestamp}.{event_id}.".encode("utf-8") + raw_body
expected = "v1=" + hmac.new(
token.encode("utf-8"), message, hashlib.sha256
).hexdigest()
return hmac.compare_digest(expected, signature)
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
boolean verify(byte[] rawBody, String timestamp, String eventId,
String signature, String authorization, String token) throws Exception {
if (!MessageDigest.isEqual(
authorization.getBytes(StandardCharsets.UTF_8),
("Bearer " + token).getBytes(StandardCharsets.UTF_8))) return false;
byte[] prefix = (timestamp + "." + eventId + ".").getBytes(StandardCharsets.UTF_8);
byte[] content = new byte[prefix.length + rawBody.length];
System.arraycopy(prefix, 0, content, 0, prefix.length);
System.arraycopy(rawBody, 0, content, prefix.length, rawBody.length);
Mac mac = Mac.getInstance("HmacSHA256");
mac.init(new SecretKeySpec(token.getBytes(StandardCharsets.UTF_8), "HmacSHA256"));
String expected = "v1=" + java.util.HexFormat.of().formatHex(mac.doFinal(content));
return MessageDigest.isEqual(expected.getBytes(StandardCharsets.UTF_8),
signature.getBytes(StandardCharsets.UTF_8));
}
import (
"crypto/hmac"
"crypto/sha256"
"fmt"
)
func verifyWebhook(rawBody []byte, timestamp, eventID, signature, authorization, token string) bool {
if !hmac.Equal([]byte(authorization), []byte("Bearer "+token)) {
return false
}
content := append([]byte(timestamp+"."+eventID+"."), rawBody...)
mac := hmac.New(sha256.New, []byte(token))
mac.Write(content)
expected := fmt.Sprintf("v1=%x", mac.Sum(nil))
return hmac.Equal([]byte(expected), []byte(signature))
}
投递与发送历史¶
同一个 Webhook 对同一故障在短时间内连续产生多个变化时,系统会合并为一次投递,并在 Payload 的 eventTypes 中返回本次包含的变化类型。Payload 始终以发送时的最新故障快照为准。
系统不自动重试,也不支持手动重试。您可以在发送历史中按发送状态、故障 UUID 或事件类型查看投递结果;失败记录会展示响应码和错误详情,便于排查第三方接收服务。