notify

package module
v0.6.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 25, 2026 License: MIT Imports: 17 Imported by: 0

README

gitee.com/idcu-go/notify

当前版本:v0.6.2

1. 定位 + 当前版本

模块类型:领域模块(多渠道告警通知,依赖领域渠道语义)。

一个可复用的 Go 告警通知模块:所有渠道统一实现 Notifier 接口,业务方只依赖接口即可发送通知;新增渠道(邮件 / 短信 / 通用 webhook)只需实现接口并在 NewNotifier 的 switch 中注册,不改调用方代码。HTTP 发送统一委托公共模块 httputil(含 10s 超时客户端与回执解析)。 内置五种渠道:

渠道 类型值 格式 鉴权
企业微信 wecom / wechat / weixin markdown,支持 <@userid> @成员 webhook
钉钉 dingtalk / dingding / 钉钉 markdown webhook + 加签(可选)
飞书 / Lark feishu / lark / 飞书 text webhook + 加签(可选)
邮件(SMTP) email / mail / 邮件 纯文本邮件(net/smtp,零依赖) SMTP 账号/密码
通用 Webhook webhook / http 默认 JSON {title,body,alert};可自定义模板对接自建网关 自定义 header(如 Bearer)
Slack slack 模板化 webhook 报文(text/blocks) webhook
Telegram telegram 模板化 webhook 报文 Bot token + chat_id
PagerDuty pagerduty Alert.Kind 自动 trigger↔resolve 的 Events API routing_key;级别由 severity 指定(缺省 critical)

2. 安装

go get gitee.com/idcu-go/notify@v0.6.2

依赖

  • 内部(gitee.com/idcu-go/*):httputiltimefmt
    • 用途:httputil 发送企微/钉钉/飞书/邮件/webhook 等渠道的 HTTP 请求;timefmt 格式化通知时间。auth(indirect)随 httputil 引入。
  • 外部(第三方):无

3. API 签名

核心接口与导出函数签名:

// 统一接口:所有渠道都实现 Type() 与 Notify()
type Notifier interface {
    Type() string
    Notify(ctx context.Context, a Alert) error
}

// 按配置构造单个 Notifier;不支持的类型返回 nil 并打 warn
func NewNotifier(nc NotifierConfig, dryRun bool) Notifier

// 注册表:[]NamedChannel -> map[name]NotifierConfig,供按名引用查找
func BuildChannelRegistry(channels []NamedChannel) map[string]NotifierConfig

// 按名字从注册表解析为 Notifier 列表(名字未定义时打 warn 并跳过)
func ResolveNamedChannels(names []string, registry map[string]NotifierConfig, dryRun bool) []Notifier

// 内联配置 -> []Notifier(兼容直接内联写法)
func BuildNotifiers(targetNotifiers []NotifierConfig, dryRun bool) []Notifier

// 门面:注册表 + 具名引用 + 内联,一步得到可用 []Notifier
// 解析优先级:names(具名引用)优先于 inline(内联),二者合并时按 Type 去重
func BuildFromConfig(channels []NamedChannel, names []string, inline []NotifierConfig, dryRun bool) []Notifier

// 加签实现(如需自定义可直接复用)
func DingTalkSign(secret string, timestamp int64) string
func FeishuSign(secret string, timestamp int64) string

// 渠道配置完整性校验(通知渠道可用性自检用:先验配置,再发测试通知)
func ConfigComplete(nc NotifierConfig) error

扩展新渠道:① 新增一个实现 Notifier 的结构体(带 Type() / Notify());② 在 NewNotifier 的 switch 中注册对应 Type;③ 如需新字段,在 NamedChannel / NotifierConfig 增加(或复用通用字段)。

4. 关键类型 / 结构体

// 告警事件(结构化),各渠道自行渲染成自己的消息格式
type Alert struct {
    Name       string        // 目标名称
    Host       string        // 目标主机
    Kind       string        // 事件类型:"down"(失联)| "recover"(恢复)| "down-still"(持续离线提醒)| "flapping"(抖动聚合)| "selfcheck"(渠道自检)
    Level      string        // 告警级别:"normal" | "escalated"(escalated 表示已升级到高级别通道,用于 down-still 升级标题)
    FailStreak int           // 连续失败次数(down/down-still/flapping 时有意义)
    DownTime   time.Duration // 中断时长(recover/down-still 时有意义)
    Time       time.Time     // 事件时间
}

// 统一接口
type Notifier interface {
    Type() string
    Notify(ctx context.Context, a Alert) error
}

// 注册表中的具名渠道:定义一次(含 webhook / mention 等),业务侧按名字引用
type NamedChannel struct {
    Name        string            `yaml:"name"`
    Type        string            `yaml:"type"`
    Webhook     string            `yaml:"webhook"`
    Mention     string            `yaml:"mention"`
    Secret      string            `yaml:"secret"`   // 钉钉/飞书加签密钥(可选)
    URL         string            `yaml:"url"`      // 通用 webhook 地址
    Headers     map[string]string `yaml:"headers"`  // 通用 webhook 自定义请求头
    Template    string            `yaml:"template"` // 通用 webhook 报文模板(Go text/template,作用于 Alert)
    EmailConfig `yaml:",inline"`  // 邮件专属字段内联
}

// 内联 notifiers 列表的精简配置(兼容写法)
type NotifierConfig struct {
    Type        string
    Webhook     string
    Mention     string
    Secret      string
    URL         string
    Headers     map[string]string
    Template    string
    EmailConfig `yaml:",inline"`
}

// 邮件专属配置(被 EmailNotifier 内嵌复用,保证「配置 ↔ 实例」字段一致)
type EmailConfig struct {
    Host    string `yaml:"host"`    // SMTP 服务器
    Port    int    `yaml:"port"`    // 端口,通常 465/587/25
    User    string `yaml:"user"`    // 发件账号
    Pass    string `yaml:"pass"`    // 授权码 / 密码
    From    string `yaml:"from"`    // 发件人
    To      string `yaml:"to"`      // 收件人(逗号分隔多个)
    Subject string `yaml:"subject"` // 主题前缀,默认 "监控告警"
}

五种渠道实现:WeComNotifierDingTalkNotifierFeishuNotifierEmailNotifierWebhookNotifier,均以 Type() / Notify() 满足 Notifier 接口,并各自持有 DryRun 字段。

5. 最小用法示例

_test.go 提炼,保留注册表用法:

// (1) 注册表:定义一次渠道,业务侧按名字引用
channels := []notify.NamedChannel{
    {Name: "ops", Type: "wecom", Webhook: "https://qyapi.weixin.qq.com/...", Mention: "zhangsan"},
    {Name: "dd",  Type: "dingtalk", Webhook: "https://oapi.dingtalk.com/...", Secret: "签名密钥"},
}
reg := notify.BuildChannelRegistry(channels)               // []NamedChannel -> map[name]NotifierConfig
ns  := notify.ResolveNamedChannels([]string{"ops", "dd"}, reg, false) // 按名字解析出 Notifier 列表

// (2) 一步到位门面:注册表 + 具名引用 + 内联,直接得到 []Notifier
inline := []notify.NotifierConfig{
    {Type: "feishu", Webhook: "https://open.feishu.cn/...", Secret: "k"},
    {Type: "email", Host: "smtp.example.com", Port: 465, User: "a@b.com",
     Pass: "授权码", From: "a@b.com", To: "c@d.com"},
}
notifiers := notify.BuildFromConfig(channels, []string{"ops", "dd"}, inline, false)
// 顺序:先 names 命中项(wecom, dingtalk),再 inline 去重后追加项(feishu, email)

// (3) 发送:遍历通知,逐渠道推送同一 Alert
a := notify.Alert{Name: "网关", Host: "1.1.1.1", Kind: "down", FailStreak: 3, Time: time.Now()}
for _, n := range notifiers {
    if err := n.Notify(ctx, a); err != nil {
        log.Printf("发送[%s]失败: %v", n.Type(), err)
    }
}

DryRun=true 时只打印不真正发送,便于本地演练。内联写法也支持:notify.BuildNotifiers([]notify.NotifierConfig{...}, dry)

6. 工作机制 / 边界语义

  • 事件去重BuildFromConfig 合并 names(具名引用)与 inline(内联)时按 Notifier.Type() 去重,相同类型只保留一个实例,避免重复推送同一渠道;返回顺序为先 names 命中项、再 inline 项(notifier.go 去重逻辑)。
  • 抖动聚合(flapping)Alert.Kind="flapping" 由上游监控在判定目标短期内频繁上下线后传入,notify 将其渲染为单条「抖动告警」文案以减少打扰;notify 本身不做状态计数/聚合,聚合职责在上游。
  • DryRun 语义:每个 Notifier.NotifyDryRun=true 时仅 log.Printf 打印内容、不发起真实请求(WeComNotifier/DingTalkNotifier/FeishuNotifier/EmailNotifier/WebhookNotifier 实现开头均判断)。
  • 超时语义:所有 HTTP 发送经公共模块 httputil,复用其 10s 超时客户端;钉钉/飞书按 errcode/code 回执解析,通用 Webhook 仅以状态码判定(2xx 成功,非 2xx 返回错误)。
  • 缺失配置不报错WeComNotifier/DingTalkNotifier/FeishuNotifier/WebhookNotifier 在 webhook/URL 为空时仅打印并返回 nilEmailNotifierhost/from/to 任一为空时仅打印返回 nil
  • 未知 / 未注册跳过NewNotifier 遇到不支持的 Type 返回 nil 并打 warn;ResolveNamedChannels 遇到注册表中不存在的名字打 warn 并跳过该渠道。
  • @ 成员自持mention 由各渠道结构体持有(如 WeComNotifier.Mention),不在 Alert 中,因此不同渠道可使用不同 @ 成员。

7. 适用场景

  • 多通道告警:同一 Alert 可同时推送企业微信 / 钉钉 / 飞书 / 邮件 / 通用 Webhook,覆盖 IM 与离线留存。
  • 抖动抑制:上游将频繁上下线聚合为 flapping 单条告警,避免逐次打扰值班人员。
  • 配置集中管理channels 注册表定义一次 webhook,业务侧按名字引用,避免 webhook 散落各处。
  • 对接第三方系统:通用 Webhook + 自定义 Template 对接 Slack / Telegram / PagerDuty / 自建告警网关。
  • 本地演练DryRun=true 只打印不发送,便于上线前验证消息渲染与路由。

8. 变更记录

见仓库 tag v0.6.2(各版本差异以 git tag 记录为准)。

Documentation

Overview

Package notify 提供一个可复用的告警通知模块:所有渠道统一实现 Notifier 接口, 监控/调度方只依赖接口即可发送通知;新增渠道(邮件 / 短信 / 通用 webhook …)只需实现接口并注册。

当前内置实现:

  • WeComNotifier 企业微信群机器人(markdown,支持 @ 成员)
  • DingTalkNotifier 钉钉自定义机器人(markdown + 加签)
  • FeishuNotifier 飞书/Lark 自定义机器人(text + 加签)
  • EmailNotifier SMTP 邮件
  • WebhookNotifier 通用 webhook(支持 Go text/template 自定义报文,兜底任意系统)
  • SlackNotifier / TelegramNotifier / PagerDutyNotifier SaaS 原生 webhook 适配(PagerDuty 按 Alert.Kind 自动 trigger↔resolve),详见 saas_channels.go

设计要点(与日志模块 logutil 同构):渠道只在「注册表」里定义一次(含 webhook), 之后在业务侧按名字引用,不必到处粘贴 webhook。

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func BuildChannelRegistry

func BuildChannelRegistry(channels []NamedChannel) map[string]NotifierConfig

BuildChannelRegistry 把 channels 注册表转成 map[name]NotifierConfig,供按名引用查找。

func ConfigComplete

func ConfigComplete(nc NotifierConfig) error

ConfigComplete 校验单个渠道配置是否具备发送所需的必填字段(不含网络连通性)。 用于「通知渠道可用性自检」:先校验配置完整性,再实际发送探测连通性, 避免「未配置 webhook 却被当作发送成功」的误报。 返回 nil 表示配置完整;否则返回缺失项/未知类型的描述。

func DingTalkSign

func DingTalkSign(secret string, timestamp int64) string

DingTalkSign 计算加签:timestamp(毫秒) + "\n" + secret,HMAC-SHA256 后 base64。

func FeishuSign

func FeishuSign(secret string, timestamp int64) string

FeishuSign 计算加签:HMAC-SHA256(secret, fmt.Sprint(timestamp)) 后 base64。

Types

type Alert

type Alert struct {
	Name       string        // 目标名称
	Host       string        // 目标主机
	Kind       string        // "down" | "recover" | "down-still" | "flapping" | "selfcheck"
	Level      string        // "normal" | "escalated"(escalated 表示已升级到高级别通道)
	FailStreak int           // 当前连续失败次数(down / down-still 时有意义)
	DownTime   time.Duration // 中断时长(recover / down-still 时有意义)
	Time       time.Time     // 事件时间
}

Alert 是发送给通知渠道的结构化事件。各渠道自行渲染成自己的消息格式。 注意:@ 成员(mention)由各渠道自己持有,不在 Alert 里,从而不同渠道可用不同 mention。

type DingTalkNotifier

type DingTalkNotifier struct {
	Webhook string
	Secret  string // 加签密钥,机器人安全设置选「加签」时必填
	DryRun  bool
}

DingTalkNotifier 钉钉自定义机器人。支持「关键词 / 加签 / IP 白名单」中的加签方式: 在 webhook 后追加 ?timestamp=...&sign=...,sign = HMAC-SHA256(secret, timestamp+"\n"+secret) 的 URL 编码。

func (DingTalkNotifier) Notify

func (n DingTalkNotifier) Notify(ctx context.Context, a Alert) error

func (DingTalkNotifier) Type

func (n DingTalkNotifier) Type() string

type EmailConfig

type EmailConfig struct {
	Host    string `yaml:"host"`    // SMTP 服务器,如 "smtp.example.com"
	Port    int    `yaml:"port"`    // SMTP 端口,通常 465(SSL) / 587(TLS) / 25
	User    string `yaml:"user"`    // 发件账号
	Pass    string `yaml:"pass"`    // 授权码 / 密码
	From    string `yaml:"from"`    // 发件人地址
	To      string `yaml:"to"`      // 收件人地址(逗号分隔多个)
	Subject string `yaml:"subject"` // 邮件主题前缀,默认 "监控告警"
}

EmailConfig 是邮件渠道专属配置,单独成组避免与 IM 渠道字段混在一起。 同时被 EmailNotifier 复用为内嵌字段,保证「配置 ↔ 实例」字段一致。

type EmailNotifier

type EmailNotifier struct {
	EmailConfig
	DryRun bool
}

EmailNotifier 通过 SMTP 发送告警邮件(标准库 net/smtp,无第三方依赖)。 适用于需要「离线也能留存」的告警通道(区别于即时 IM 机器人)。

func (EmailNotifier) Notify

func (n EmailNotifier) Notify(ctx context.Context, a Alert) error

Notify 发送一封告警邮件。DryRun 时仅打印,不真正发出。

func (EmailNotifier) Type

func (n EmailNotifier) Type() string

type FeishuNotifier

type FeishuNotifier struct {
	Webhook string
	Secret  string // 加签密钥(安全设置选「签名校验」时必填)
	DryRun  bool
}

FeishuNotifier 飞书/ Lark 自定义机器人。加签方式:请求体携带 timestamp 与 sign = HMAC-SHA256(key=secret, data=时间戳字符串) 的 base64。

func (FeishuNotifier) Notify

func (n FeishuNotifier) Notify(ctx context.Context, a Alert) error

func (FeishuNotifier) Type

func (n FeishuNotifier) Type() string

type NamedChannel

type NamedChannel struct {
	Name        string            `yaml:"name"` // 渠道唯一名字,供引用
	Type        string            `yaml:"type"` // "wecom" | "dingtalk" | "feishu"(或 "lark") | "email" | "webhook" | "slack" | "telegram" | "pagerduty"
	Webhook     string            `yaml:"webhook"`
	Mention     string            `yaml:"mention"`
	Secret      string            `yaml:"secret"`      // 可选:钉钉/飞书签名密钥(加签机器人需要)
	URL         string            `yaml:"url"`         // 通用 webhook 地址
	Headers     map[string]string `yaml:"headers"`     // 通用 webhook 自定义请求头
	Template    string            `yaml:"template"`    // 通用 webhook 报文模板(Go text/template,作用于 Alert)
	Channel     string            `yaml:"channel"`     // slack:目标频道
	Token       string            `yaml:"token"`       // telegram:Bot token
	ChatID      string            `yaml:"chat_id"`     // telegram:目标 chat_id
	RoutingKey  string            `yaml:"routing_key"` // pagerduty:Integration Key
	Severity    string            `yaml:"severity"`    // pagerduty:事件级别(缺省 critical)
	EmailConfig `yaml:",inline"`  // 邮件(email)专属字段,内联展开
}

NamedChannel 是「渠道注册表(channels)」里的一个具名渠道: 先在此用名字定义一次(含 webhook / mention 等),之后在 defaults / target 里 只要写 channels: [名字] 引用即可,无需重复粘贴 webhook。

type Notifier

type Notifier interface {
	Type() string
	Notify(ctx context.Context, a Alert) error
}

Notifier 是所有通知渠道的统一接口。

func BuildFromConfig

func BuildFromConfig(channels []NamedChannel, names []string, inline []NotifierConfig, dryRun bool) []Notifier

BuildFromConfig 是「注册表 → 解析 → 构造」一步到位的门面,覆盖 90% 调用方的需求: 传入具名渠道注册表 channels、引用了渠道名的 names、以及内联 notifiers, 直接得到可用的 []Notifier。等价于先 BuildChannelRegistry 再 ResolveNamedChannels / BuildNotifiers。

解析优先级(与 Monitor.toMonitor 的约定保持一致):

names(具名引用)优先于 inline(内联),二者都提供时合并去重。

返回列表顺序:先 names 命中项,再 inline 项。dryRun 透传给每个 Notifier。

func BuildNotifiers

func BuildNotifiers(targetNotifiers []NotifierConfig, dryRun bool) []Notifier

BuildNotifiers 把内联 notifiers 列表(兼容写法)构造为 Notifier 列表。 仅处理显式内联的渠道;全局/遗留默认已移除,渠道统一走 channels 注册表。

func NewNotifier

func NewNotifier(nc NotifierConfig, dryRun bool) Notifier

NewNotifier 根据配置构造一个 Notifier 实例;不支持的类型返回 nil(并打警告)。

func ResolveNamedChannels

func ResolveNamedChannels(names []string, registry map[string]NotifierConfig, dryRun bool) []Notifier

ResolveNamedChannels 按名字从注册表查到渠道配置,构造 Notifier 列表。 名字在注册表中不存在时打警告并跳过该渠道。

type NotifierConfig

type NotifierConfig struct {
	Type        string            `yaml:"type"`
	Webhook     string            `yaml:"webhook"`
	Mention     string            `yaml:"mention"`
	Secret      string            `yaml:"secret"`
	URL         string            `yaml:"url"`
	Headers     map[string]string `yaml:"headers"`
	Template    string            `yaml:"template"`
	Channel     string            `yaml:"channel"`
	Token       string            `yaml:"token"`
	ChatID      string            `yaml:"chat_id"`
	RoutingKey  string            `yaml:"routing_key"`
	Severity    string            `yaml:"severity"`
	EmailConfig `yaml:",inline"`  // 邮件(email)专属字段,内联展开
}

NotifierConfig 是单个渠道的精简配置,用于「内联 notifiers 列表」兼容写法。

type PagerDutyNotifier

type PagerDutyNotifier struct {
	RoutingKey string // Integration Key(Events API v2 routing_key)
	Severity   string // 事件级别:critical|error|warning|info;空默认 critical
	DryRun     bool
}

PagerDutyNotifier PagerDuty 渠道(Events API v2)。 根据 Alert.Kind 自动映射事件动作:down / down-still → trigger,recover → resolve, 确保一次事件只产生 / 只关闭一条 incident。

func (PagerDutyNotifier) Notify

func (n PagerDutyNotifier) Notify(ctx context.Context, a Alert) error

func (PagerDutyNotifier) Type

func (n PagerDutyNotifier) Type() string

type SlackNotifier

type SlackNotifier struct {
	Webhook string // https://hooks.slack.com/services/...
	Channel string // 可选:目标频道,如 #alerts
	DryRun  bool
}

SlackNotifier Slack 渠道。把统一渲染文案 POST 到 incoming webhook。

func (SlackNotifier) Notify

func (n SlackNotifier) Notify(ctx context.Context, a Alert) error

func (SlackNotifier) Type

func (n SlackNotifier) Type() string

type TelegramNotifier

type TelegramNotifier struct {
	Token  string // Bot token,形如 123456:ABC-...
	ChatID string // 目标 chat_id(群/私聊),可传数字或 @频道名
	DryRun bool
}

TelegramNotifier Telegram 渠道。通过 Bot token 调官方 sendMessage 接口。

func (TelegramNotifier) Notify

func (n TelegramNotifier) Notify(ctx context.Context, a Alert) error

func (TelegramNotifier) Type

func (n TelegramNotifier) Type() string

type WeComNotifier

type WeComNotifier struct {
	Webhook string
	Mention string // 本渠道专用的 @ 成员
	DryRun  bool
}

WeComNotifier 企业微信渠道实现(当前默认渠道)。@ 成员自持于本结构体。

func (WeComNotifier) Notify

func (n WeComNotifier) Notify(ctx context.Context, a Alert) error

func (WeComNotifier) Type

func (n WeComNotifier) Type() string

type WebhookNotifier

type WebhookNotifier struct {
	URL     string            // 目标 webhook 地址(必填)
	Headers map[string]string // 可选:自定义请求头(如 Authorization / Content-Type)
	// Template 可选:Go text/template 字符串,作用于 Alert,产出请求体。
	// 不设置则发送默认 JSON。模板内可用 .Name .Host .Kind .Level .FailStreak .DownTime .Time。
	Template string
	DryRun   bool
}

WebhookNotifier 是「通用 Webhook」渠道:向任意 HTTP 端点 POST 一条消息, 用于对接 Slack / Telegram / PagerDuty / 企业自建告警网关等任意系统, 是补齐「通知渠道数量」差距的通用兜底(无需为每个平台写专用适配器)。

默认发送 JSON:{"title":..., "body":..., "alert":{...Alert 原样}}。 如需自定义报文,可设置 Template(Go text/template,作用对象为 Alert)。

func (WebhookNotifier) Notify

func (n WebhookNotifier) Notify(ctx context.Context, a Alert) error

func (WebhookNotifier) Type

func (n WebhookNotifier) Type() string

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL