Go脚本+夜莺回调地址实现邮件告警
本例子演示的夜莺监控版本为v5.8.0,如图:

说明:如果将夜莺监控部署在纯内网,想要实现邮件告警只能通过代理tcp端口(比如25),如果内网和外网的tcp端口不能代理,并且内网和外网之间只能通过80端口通信,那么代理tcp端口方式就比较困难,此时可以通过回调地址来实现自定义告警,调用回调地址,然后通过Nginx代理出去,将脚本放到外网机器即可,首先可以看到夜莺告警规则的回调地址位置如下,这里我已经填写了我自己的地址,如图:

默认情况下,触发告警的时候,发送给回调地址的JSON数据格式如下:
{"id":1,"cluster":"Default","group_id":1,"group_name":"Default Busi Group","hash":"d533f164ecde00cf16bbb435c18d6b62","rule_id":1,"rule_name":"nginx80端口Down","rule_note":"test_machine","rule_prod":"","rule_algo":"","severity":3,"prom_for_duration":60,"prom_ql":"net_response_result_code{ident=\"192.168.51.246\", protocol=\"tcp\", target=\"127.0.0.1:80\"} != 0","prom_eval_interval":15,"callbacks":["http://127.0.0.1:4321"],"runbook_url":"","notify_recovered":1,"notify_channels":[],"notify_groups":[],"notify_groups_obj":[],"target_ident":"192.168.51.246","target_note":"","trigger_time":1733988554,"trigger_value":"2","tags":["__name__=net_response_result_code","ident=192.168.51.246","protocol=tcp","target=127.0.0.1:80"],"is_recovered":false,"notify_users_obj":[],"last_eval_time":1733988554,"last_sent_time":1733988554}
告警恢复的时候,发送给回调地址的JSON数据格式如下:
告警和恢复的数据格式都是JSON,只需要提取其中的部分字段处理后即可实现自定义邮件告警,数据发送到回调地址是通过POST方法请求,因此golang脚本也需要定义POST方法,原理如下:
JSON数据通过post请求发送给golang脚本,脚本获取数据后,将数据通过ShouldBindJSON绑定到结构体对应字段中(注意:字段大小写没关系,但是格式和内容必须对应),绑定后开始判断Is_recovered是true还是false,如果是false说明是触发告警,否则就是告警恢复,然后将内容通过邮件发送即可
脚本内容如下:
package main
import (
"fmt"
"github.com/gin-gonic/gin"
"github.com/jordan-wright/email"
"log"
"net/smtp"
"time"
"io/ioutil"
"gopkg.in/yaml.v2"
)
type test struct {
ID int //id
GroupID int //组id
Cluster string //所属集群
Group_name string //组名
Rule_name string //告警策略名
Rule_note string //告警策略备注
Severity int //告警级别
Trigger_value string //触发时候的值,当不为0的时候就会触发告警
Target_ident string //当前告警的服务器ip地址
Trigger_time int //触发时间
Is_recovered bool //是否恢复
RecoveredTime int //恢复时间
AlarmStatus string //告警状态
}
type mailConfig struct {
Send_mail string //发件人
Recv_mail []string //收件人,可以多个,所以用字符串切片
Cc_mail []string //抄送人,也可以多个
Smtp_address string //smtp服务器地址
Smtp_port int //smtp端口
Auth_code string //授权码
}
func createMail() *mailConfig{
var mc *mailConfig
by,err := ioutil.ReadFile("/etc/monitor.yaml")
if err != nil {
log.Fatal(err)
}
err = yaml.Unmarshal(by,&mc)
if err != nil {
log.Fatal(err)
}
return mc
}
func sendmail(id, groupid, severity int, cluster, groupname, rulename, rulenote, targetident, triggertime, triggervalue, recoveredtime, alarmstatus string, isrecovered bool) {
mc := createMail()
e := email.NewEmail()
//设置发送方的邮箱
e.From = mc.Send_mail
// 设置接收方的邮箱
e.To = mc.Recv_mail
e.Cc = mc.Cc_mail
//设置主题
e.Subject = alarmstatus
//设置文件发送的内容
text := fmt.Sprintf("ID:%d\n组ID:%d\n所属集群:%s\n组名:%s\n告警级别:%d\n告警规则:%s\n告警规则备注:%s\n告警设备:%s\n告警时间:%s\n当前值:%s\n是否恢复:%v\n恢复时间:%s", id, groupid, cluster,groupname, severity, rulename, rulenote, targetident, triggertime, triggervalue,isrecovered, recoveredtime)
e.Text = []byte(text)
//设置服务器相关的配置
e.Send(fmt.Sprintf("%s:%d",mc.Smtp_address,mc.Smtp_port),smtp.PlainAuth("",mc.Send_mail,mc.Auth_code,mc.Smtp_address))
}
// 将时间戳转换为标准时间
func timestamp(t int) string {
tint64 := int64(t)
ts := time.Unix(tint64, 0)
return ts.Format("2006-01-02 15:04:05")
}
func main() {
r := gin.New()
r.POST("/json", func(c *gin.Context) {
t := &test{}
err := c.ShouldBindJSON(t)
if err != nil {
log.Fatal(err)
}
fmt.Println("Datasource:",t)
id := t.ID
groupid := t.GroupID
severity := t.Severity
cluster := t.Cluster
groupname := t.Group_name
rulename := t.Rule_name
rulenote := t.Rule_note
targetident := t.Target_ident
triggervalue := t.Trigger_value
triggertime := timestamp(t.Trigger_time)
isrecovered := t.Is_recovered
recoveredtime := ""
alarmstatus := ""
//如果为true,说明已经收到了告警恢复的回调数据
if t.Is_recovered {
recoveredtime = time.Now().Format("2006-01-02 15:04:05")
alarmstatus = fmt.Sprintf("告警恢复:%s",t.Rule_name)
} else {
alarmstatus = fmt.Sprintf("告警触发:%s",t.Rule_name)
}
sendmail(id, groupid, severity, cluster, groupname, rulename, rulenote, targetident, triggertime, triggervalue, recoveredtime, alarmstatus, isrecovered)
})
r.Run(":8080")
}
说明:告警恢复的回调地址数据中没有恢复时间,因此我手动添加了一个,通过判断传递过来的Is_recovered如果是true,说明恢复了,此时设置下recoveredtime为当前时间,这样恢复内容就可以显示触发时间和恢复时间了
配置文件monitor.yaml是单独定义的,可以方便后续修改发件人、授权码以及收件人,配置文件内容如下:
#发件人
send_mail: 88994433@qq.com
#收件人邮箱,可写多个
recv_mail:
- def@qq.com
#抄送人邮箱
cc_mail:
- abcd2023@outlook.com
#smtp服务器地址
smtp_address: smtp.qq.com
#smtp端口
smtp_port: 25
#授权码
auth_code: aucoabcdeituwgegh
最后实现告警截图如下:

告警恢复如下,有恢复时间:



