Skip to content

Commit

Permalink
🚀 Notify object supported WeChat and CustomHook types
Browse files Browse the repository at this point in the history
  • Loading branch information
Cairry committed Dec 14, 2024
1 parent f87271c commit e5924ea
Show file tree
Hide file tree
Showing 18 changed files with 272 additions and 95 deletions.
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
</p>

<p align="center">
<b>WatchAlert 开源一站式监控告警管理系统</b>
<b>WatchAlert 开源一站式多数据源监控告警引擎</b>
</p>

<p align="center">
Expand All @@ -18,7 +18,7 @@
<img alt="License" src="https://img.shields.io/badge/license-Apache--2.0-blue"/>

## 💎 项目介绍
WatchAlert 是一款为云原生环境量身打造的轻量级监控告警系统,专注于**可观测稳定性**主题,提供全面的监控与告警支持。
WatchAlert 是一款为云原生环境量身打造的轻量级监控告警引擎,专注于**可观测稳定性**主题,提供全面的监控与告警支持。

**能力**
- Metrics 监控
Expand All @@ -31,6 +31,8 @@ WatchAlert 是一款为云原生环境量身打造的轻量级监控告警系统
- 集成:Kubernetes
- Network 监控
- 集成:HTTP、ICMP、TCP、SSL
- 告警通知
- 飞书、钉钉、企业微信、邮件、自定义Hook


**为什么选择 WatchAlert?**
Expand All @@ -49,7 +51,7 @@ WatchAlert 是一款为云原生环境量身打造的轻量级监控告警系统
- 演示环境:http://8.147.234.89/login
(admin/123)

| ![登陆页](assets/login.png) | ![首页](assets/home.png) |
| ![登陆页](assets/login.png) | ![首页](assets/home.png) |
|:------------------------------:|--------------------------------|
| ![img.png](assets/img.png) | ![img_1.png](assets/img_1.png) |
| ![img_2.png](assets/img_2.png) | ![img_3.png](assets/img_3.png) |
Expand Down
11 changes: 8 additions & 3 deletions alert/consumer/consumer.go
Original file line number Diff line number Diff line change
Expand Up @@ -302,8 +302,13 @@ func (ec *Consume) handleAlert(rule models.AlertRule, alerts []models.AlertCurEv
return
}

n := templates.NewTemplate(ec.ctx, alert, noticeData)
err := sender.Sender(ec.ctx, sender.SendParmas{
var content string
if noticeData.NoticeType == "CustomHook" {
content = tools.JsonMarshal(alert)
} else {
content = templates.NewTemplate(ec.ctx, alert, noticeData).CardContentMsg
}
err := sender.Sender(ec.ctx, sender.SendParams{
TenantId: alert.TenantId,
RuleName: alert.RuleName,
Severity: alert.Severity,
Expand All @@ -313,7 +318,7 @@ func (ec *Consume) handleAlert(rule models.AlertRule, alerts []models.AlertCurEv
IsRecovered: alert.IsRecovered,
Hook: noticeData.Hook,
Email: noticeData.Email,
Content: n.CardContentMsg,
Content: content,
Event: nil,
})
if err != nil {
Expand Down
10 changes: 9 additions & 1 deletion alert/consumer/subscribe.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,15 @@ func processSubscribe(ctx *ctx.Context, alert models.AlertCurEvent, notice model
notice.NoticeTmplId = u.NoticeTemplateId
emailTemp := templates.NewTemplate(ctx, alert, notice)

err = sender.SendToEmail(alert.IsRecovered, u.NoticeSubject, []string{u.Email}, nil, emailTemp.CardContentMsg)
err = sender.NewEmailSender().Send(sender.SendParams{
IsRecovered: alert.IsRecovered,
Email: models.Email{
Subject: u.NoticeSubject,
To: []string{u.Email},
CC: nil,
},
Content: emailTemp.CardContentMsg,
})
if err != nil {
return fmt.Errorf("邮件发送失败, err: %s", err.Error())
}
Expand Down
12 changes: 9 additions & 3 deletions alert/probing/consumer.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"watchAlert/pkg/ctx"
"watchAlert/pkg/sender"
"watchAlert/pkg/templates"
"watchAlert/pkg/tools"
)

type ConsumeProbing struct {
Expand Down Expand Up @@ -69,8 +70,13 @@ func (m *ConsumeProbing) handleAlert(alert models.ProbingEvent) {
noticeData, _ := ctx.DB.Notice().Get(r)
alert.DutyUser = process.GetDutyUser(m.ctx, noticeData)

n := templates.NewTemplate(m.ctx, buildEvent(alert), noticeData)
err := sender.Sender(m.ctx, sender.SendParmas{
var content string
if noticeData.NoticeType == "CustomHook" {
content = tools.JsonMarshal(alert)
} else {
content = templates.NewTemplate(m.ctx, buildEvent(alert), noticeData).CardContentMsg
}
err := sender.Sender(m.ctx, sender.SendParams{
TenantId: alert.TenantId,
Severity: alert.Severity,
NoticeType: noticeData.NoticeType,
Expand All @@ -79,7 +85,7 @@ func (m *ConsumeProbing) handleAlert(alert models.ProbingEvent) {
IsRecovered: alert.IsRecovered,
Hook: noticeData.Hook,
Email: noticeData.Email,
Content: n.CardContentMsg,
Content: content,
Event: nil,
})
if err != nil {
Expand Down
17 changes: 8 additions & 9 deletions alert/process/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -161,20 +161,19 @@ func GetNoticeGroupId(alert models.AlertCurEvent) string {
}

func GetDutyUser(ctx *ctx.Context, noticeData models.AlertNotice) string {
user := ctx.DB.DutyCalendar().GetDutyUserInfo(noticeData.DutyId, time.Now().Format("2006-1-2"))
switch noticeData.NoticeType {
case "FeiShu":
// 判断是否有安排值班人员
if len(user.DutyUserId) > 1 {
user, ok := ctx.DB.DutyCalendar().GetDutyUserInfo(noticeData.DutyId, time.Now().Format("2006-1-2"))
if ok {
switch noticeData.NoticeType {
case "FeiShu":
return fmt.Sprintf("<at id=%s></at>", user.DutyUserId)
}
case "DingDing":
if len(user.DutyUserId) > 1 {
case "DingDing":
return fmt.Sprintf("%s", user.DutyUserId)
case "Email", "WeChat", "CustomHook":
return fmt.Sprintf("@%s", user.UserName)
}
}

return ""
return "暂无"
}

// RecordAlertHisEvent 记录历史告警
Expand Down
Binary file modified assets/login.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
File renamed without changes.
File renamed without changes.
10 changes: 10 additions & 0 deletions internal/models/template_wechat.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package models

type WeChatMsgTemplate struct {
MsgType string `json:"msgtype"`
MarkDown WeChatMarkDown `json:"markdown"`
}

type WeChatMarkDown struct {
Content string `json:"content"`
}
19 changes: 11 additions & 8 deletions internal/repo/duty_schedule.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ type (

InterDutyCalendar interface {
GetCalendarInfo(dutyId, time string) models.DutySchedule
GetDutyUserInfo(dutyId, time string) models.Member
GetDutyUserInfo(dutyId, time string) (models.Member, bool)
Create(r models.DutySchedule) error
Update(r models.DutySchedule) error
Search(r models.DutyScheduleQuery) ([]models.DutySchedule, error)
Expand Down Expand Up @@ -43,16 +43,19 @@ func (dc DutyCalendarRepo) GetCalendarInfo(dutyId, time string) models.DutySched
}

// GetDutyUserInfo 获取值班用户信息
func (dc DutyCalendarRepo) GetDutyUserInfo(dutyId, time string) models.Member {
func (dc DutyCalendarRepo) GetDutyUserInfo(dutyId, time string) (models.Member, bool) {
var user models.Member

schedule := dc.GetCalendarInfo(dutyId, time)
db := dc.db.Model(models.Member{}).
Where("user_id = ?", schedule.UserId)
if err := db.First(&user).Error; err != nil {
return user, false
}
if user.JoinDuty == "true" {
return user, true
}

dc.db.Model(models.Member{}).
Where("user_id = ?", schedule.UserId).
First(&user)

return user
return user, false
}

func (dc DutyCalendarRepo) Create(r models.DutySchedule) error {
Expand Down
23 changes: 16 additions & 7 deletions pkg/sender/dingding.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,28 @@ import (
"watchAlert/pkg/tools"
)

type DingResponseMsg struct {
Code int `json:"errcode"`
Msg string `json:"errmsg"`
type (
// DingDingSender 钉钉发送策略
DingDingSender struct{}

DingResponse struct {
Code int `json:"errcode"`
Msg string `json:"errmsg"`
}
)

func NewDingSender() SendInter {
return &DingDingSender{}
}

func SendToDingDing(hook, msg string) error {
cardContentByte := bytes.NewReader([]byte(msg))
res, err := tools.Post(nil, hook, cardContentByte, 10)
func (d *DingDingSender) Send(params SendParams) error {
cardContentByte := bytes.NewReader([]byte(params.Content))
res, err := tools.Post(nil, params.Hook, cardContentByte, 10)
if err != nil {
return err
}

var response DingResponseMsg
var response DingResponse
if err := tools.ParseReaderBody(res.Body, &response); err != nil {
return errors.New(fmt.Sprintf("Error unmarshalling Dingding response: %s", err.Error()))
}
Expand Down
19 changes: 13 additions & 6 deletions pkg/sender/email.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,27 @@ import (
"watchAlert/pkg/ctx"
)

func SendToEmail(IsRecovered bool, subject string, to, cc []string, msg string) error {
// EmailSender 邮件发送策略
type EmailSender struct{}

func NewEmailSender() SendInter {
return &EmailSender{}
}

func (e *EmailSender) Send(params SendParams) error {
setting, err := ctx.DB.Setting().Get()
if err != nil {
return errors.New("获取系统配置失败: " + err.Error())
}
eCli := client.NewEmailClient(setting.EmailConfig.ServerAddress, setting.EmailConfig.Email, setting.EmailConfig.Token, setting.EmailConfig.Port)
if IsRecovered {
subject = subject + "「已恢复」"
if params.IsRecovered {
params.Email.Subject = params.Email.Subject + "「已恢复」"
} else {
subject = subject + "「报警中」"
params.Email.Subject = params.Email.Subject + "「报警中」"
}
err = eCli.Send(to, cc, subject, []byte(msg))
err = eCli.Send(params.Email.To, params.Email.CC, params.Email.Subject, []byte(params.Content))
if err != nil {
return fmt.Errorf("%s, %s", err.Error(), "Content: "+msg)
return fmt.Errorf("%s, %s", err.Error(), "Content: "+params.Content)
}

return nil
Expand Down
113 changes: 65 additions & 48 deletions pkg/sender/entry.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,66 +8,83 @@ import (
"watchAlert/pkg/ctx"
)

type SendParmas struct {
// 基础
TenantId string
RuleName string
Severity string
// 通知
NoticeType string
NoticeId string
NoticeName string
// 恢复通知
IsRecovered bool
// hook 地址
Hook string
// 邮件
Email models.Email
// 消息
Content string
// 事件
Event interface{}
type (
// SendParams 定义发送参数
SendParams struct {
// 基础
TenantId string
RuleName string
Severity string
// 通知
NoticeType string
NoticeId string
NoticeName string
// 恢复通知
IsRecovered bool
// hook 地址
Hook string
// 邮件
Email models.Email
// 消息
Content string
// 事件
Event interface{}
}

// SendInter 发送通知的接口
SendInter interface {
Send(params SendParams) error
}
)

// Sender 发送通知的主函数
func Sender(ctx *ctx.Context, sendParams SendParams) error {
// 根据通知类型获取对应的发送器
sender, err := senderFactory(sendParams.NoticeType)
if err != nil {
return fmt.Errorf("Send alarm failed, %s", err.Error())
}

// 发送通知
if err := sender.Send(sendParams); err != nil {
addRecord(ctx, sendParams, 1, sendParams.Content, err.Error())
return fmt.Errorf("Send alarm failed to %s, err: %s", sendParams.NoticeType, err.Error())
}

// 记录成功发送的日志
addRecord(ctx, sendParams, 0, sendParams.Content, "")
logc.Info(ctx.Ctx, fmt.Sprintf("Send alarm ok, msg: %s", sendParams.Content))
return nil
}

func Sender(ctx *ctx.Context, sendParmas SendParmas) error {
NoticeType := sendParmas.NoticeType
var sendFunc func() error
switch NoticeType {
// senderFactory 创建发送器的工厂函数
func senderFactory(noticeType string) (SendInter, error) {
switch noticeType {
case "Email":
sendFunc = func() error {
return SendToEmail(sendParmas.IsRecovered, sendParmas.Email.Subject, sendParmas.Email.To, sendParmas.Email.CC, sendParmas.Content)
}
return NewEmailSender(), nil
case "FeiShu":
sendFunc = func() error {
return SendToFeiShu(sendParmas.Hook, sendParmas.Content)
}
return NewFeiShuSender(), nil
case "DingDing":
sendFunc = func() error {
return SendToDingDing(sendParmas.Hook, sendParmas.Content)
}
return NewDingSender(), nil
case "WeChat":
return NewWeChatSender(), nil
case "CustomHook":
return NewWebHookSender(), nil
default:
return fmt.Errorf("Send alarm failed, exist 无效的通知类型: %s", sendParmas.NoticeType)
return nil, fmt.Errorf("无效的通知类型: %s", noticeType)
}

if err := sendFunc(); err != nil {
addRecord(ctx, sendParmas, 1, sendParmas.Content, err.Error())
return fmt.Errorf("Send alarm failed to %s, err: %s", sendParmas.NoticeType, err.Error())
}

addRecord(ctx, sendParmas, 0, sendParmas.Content, "")
logc.Info(ctx.Ctx, fmt.Sprintf("Send alarm ok, msg: %s", sendParmas.Content))
return nil
}

func addRecord(ctx *ctx.Context, sendParmas SendParmas, status int, msg, errMsg string) {
// addRecord 记录通知发送结果
func addRecord(ctx *ctx.Context, sendParams SendParams, status int, msg, errMsg string) {
err := ctx.DB.Notice().AddRecord(models.NoticeRecord{
Date: time.Now().Format("2006-01-02"),
CreateAt: time.Now().Unix(),
TenantId: sendParmas.TenantId,
RuleName: sendParmas.RuleName,
NType: sendParmas.NoticeType,
NObj: sendParmas.NoticeName + " (" + sendParmas.NoticeId + ")",
Severity: sendParmas.Severity,
TenantId: sendParams.TenantId,
RuleName: sendParams.RuleName,
NType: sendParams.NoticeType,
NObj: sendParams.NoticeName + " (" + sendParams.NoticeId + ")",
Severity: sendParams.Severity,
Status: status,
AlarmMsg: msg,
ErrMsg: errMsg,
Expand Down
Loading

0 comments on commit e5924ea

Please sign in to comment.