Go服务应直接调用PagerDuty v2 Events API(https://events.pagerduty.com/v2/enqueue),禁用已归档的pagerduty-go SDK;需设Content-Type、Authorization: Token及routing_key(Integration Key),通过event_action和payload.severity区分操作与级别,配合超时、重试、secret管理实现可靠告警。

Go 服务直接调用 PagerDuty API 发送告警是可行的,但必须绕开官方 SDK 的坑——pagerduty-go 已归档、不维护,且默认使用 v1 API(已停用),v2 接口需手动构造请求或换用社区轻量封装。
用 http.Client 直接调用 PagerDuty v2 Events API
这是最可控、最轻量的方式,适合告警逻辑简单、不想引入不稳定依赖的场景。PagerDuty 当前只支持 v2 Events API(https://events.pagerduty.com/v2/enqueue)接收告警事件,所有字段必须严格符合 schema。
- 必须设置
Content-Type: application/json和Authorization: Token token=xxx(注意不是 Bearer) -
routing_key是必填项,对应 PagerDuty 中的 Integration Key(不是 API Key),需在 UI 创建「Events API v2」类型的集成获取 - 告警级别通过
payload.severity控制:critical、error、warning、info,不填默认critical - 触发(trigger)、解决(resolve)、确认(acknowledge)用同一 endpoint,靠
event_action字段区分
reqBody := map[string]interface{}{
"routing_key": "abc123...",
"event_action": "trigger",
"payload": map[string]interface{}{
"summary": "service crashed on host-01",
"severity": "critical",
"source": "api-service",
"custom_details": map[string]string{"trace_id": "tr-789"},
},
"links": []map[string]string{{"href": "https://grafana.example.com/d/abc", "text": "Grafana dashboard"}},
}
data, _ := json.Marshal(reqBody)
resp, _ := http.Post("https://events.pagerduty.com/v2/enqueue", "application/json", bytes.NewBuffer(data))
避免用 github.com/PagerDuty/go-pagerduty
这个库名义上是 PagerDuty 官方维护,实则长期未更新,其 Incident 相关接口仍指向已下线的 v1 REST API;且没有对 Events API v2 的封装,强行用它发告警会返回 404 Not Found 或 401 Unauthorized(因鉴权方式不匹配)。
使用 uber-go/dig 在 Golang 中实现依赖注入——基于反射容器,提供 Provide/Invoke、dig.In/dig.Out 参数与结果对象,支持命名依赖等
- 它的
Client.CreateIncident走的是/incidents路径,属于旧管理类 API,不能用于实时告警 - 即使你只用它发 HTTP 请求,内部也硬编码了过时的 User-Agent 和 Accept 头,部分响应解析逻辑已失效
- 若项目里已引入,建议立即替换为裸
http.Client或自建 minimal wrapper
封装一个带重试和上下文控制的告警函数
生产环境不能裸调 http.Post:网络抖动、PagerDuty 短时不可用会导致告警丢失。需加基础容错,但不必上 full-fledged SDK。
立即学习“go语言免费学习笔记(深入)”;
- 用
context.WithTimeout控制单次请求上限(建议 ≤5s),避免阻塞主流程 - 失败时最多重试 2 次,间隔用
time.Sleep指数退避(如 1s → 2s),不要用backoff.Retry这类重型库 - 记录原始响应体(
resp.Body)而非仅状态码,PagerDuty 错误详情(如"invalid routing_key")全在 body 里 - 把
routing_key和http.Client抽成 struct 字段,方便测试 mock
type PagerDutyAlert struct {
client *http.Client
key string
timeout time.Duration
}
func (p *PagerDutyAlert) Trigger(ctx context.Context, summary string, details map[string]string) error {
body := map[string]interface{}{
"routing_key": p.key,
"event_action": "trigger",
"payload": map[string]interface{}{
"summary": summary,
"severity": "critical",
"source": "go-service",
"custom_details": details,
},
}
// ... marshal + request with ctx and retry logic
}
真正容易被忽略的是 routing_key 的来源管理——它不该硬编码在代码里,也不该从环境变量直读(易泄露),最好通过 secret manager 注入,且每个服务/环境应配独立的 Integration,否则告警混杂、无法精准路由。

















