Skip to content

Commit

Permalink
Merge pull request #993 from kl7sn/main
Browse files Browse the repository at this point in the history
feat: added the webhook alarm push channel
  • Loading branch information
kl7sn committed May 16, 2023
2 parents 0b33b69 + 4ffcb1b commit f667009
Show file tree
Hide file tree
Showing 17 changed files with 217 additions and 90 deletions.
8 changes: 7 additions & 1 deletion .github/workflows/apis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,12 +21,18 @@ jobs:
go version
go install github.com/gotomicro/ego-gen-api/cmd/egogen@v0.0.6
egogen --config egogen.yaml
- name: Install ReDoc
run: npm i -g redoc-cli
- name: Gen HTML
run: |
redoc-cli bundle ./api/docs/swagger.json ./api/docs/index.html
mv redoc-static.html ./api/docs/index.html
- name: Install obsutil
run: wget https://obs-community.obs.cn-north-1.myhuaweicloud.com/obsutil/current/obsutil_linux_amd64.tar.gz && tar -zxvf obsutil_linux_amd64.tar.gz && chmod 755 ./obsutil_linux_amd64_5.4.11/obsutil
- name: Config ossutil
run: ./obsutil_linux_amd64_5.4.11/obsutil config -i ${{ secrets.OBS_ACCESS_KEY_ID }} -k ${{ secrets.OBS_ACCESS_KEY_SECRET }} -e https://obs.cn-north-4.myhuaweicloud.com
- name: Upload docs by obsutil
run: ./obsutil_linux_amd64_5.4.11/obsutil cp ./api/docs/swagger.json obs://open-read/clickvisual/api/swagger.json -f -r
run: ./obsutil_linux_amd64_5.4.11/obsutil cp ./api/docs/index.html obs://open-read/clickvisual/api/index.html -f -r
- name: Install ossutil
run: wget http://gosspublic.alicdn.com/ossutil/1.7.3/ossutil64 && chmod 755 ossutil64
- name: Config ossutil
Expand Down
19 changes: 0 additions & 19 deletions .github/workflows/stale.yml

This file was deleted.

41 changes: 21 additions & 20 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,43 +13,44 @@ sketch
*.log
*.swp
*.swo
*.tar.gz
*.sys


### root folder
/tests
/bin
/api/bin

/logs
/rules
/configs
/api/bin
/api/logs

*.tar.gz
/data/k8s/fluent-bit/
/data/all-in-one/kafka/data/
/data/all-in-one/zookeeper/data/
/data/all-in-one/zookeeper/datalog/
/data/all-in-one/clickhouse/database/

*.sys
configs/
### inner folder
logs/

dist/
node_modules/
/dist
/ui/dist
api/internal/ui/dist/*
!api/internal/ui/dist/README.md

.umi/
.umi-production/
package-lock.json

data/all-in-one/clickhouse/database/
data/all-in-one/kafka/data/
data/all-in-one/zookeeper/data/
data/all-in-one/zookeeper/datalog/
### necessary file
!api/internal/ui/dist/README.md

### file
package-lock.json
api/docs/index.html

rules/*
/data/helm/clickvisual/default.toml
/data/helm/clickvisual/resource.yaml

/data/k8s/fluent-bit/

### ci tools
ossutil64
ossutil64.1

obsutil_linux_amd64*/

6 changes: 4 additions & 2 deletions api/internal/service/alarm.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"time"

"github.com/ego-component/egorm"
"github.com/gotomicro/ego/core/econf"
"github.com/gotomicro/ego/core/elog"
"github.com/pkg/errors"
"gorm.io/gorm"
Expand Down Expand Up @@ -704,8 +705,9 @@ func SendTestToChannel(c *db.AlarmChannel) (err error) {
return
}
err = ci.Send(c, &db.PushMsg{
Title: "Hello",
Text: "test/alert/alarm/告警 the availability of the alarm channel",
Title: "Hello",
Text: "test/alert/alarm/告警 the availability of the alarm channel",
Mobiles: econf.GetStringSlice("app.mobiles"),
})
return
}
Expand Down
15 changes: 0 additions & 15 deletions api/internal/service/alarm/pusher/email.go

This file was deleted.

105 changes: 94 additions & 11 deletions api/internal/service/alarm/pusher/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ func GetPusher(typ int) (IPusher, error) {
return &FeiShu{}, nil
case db.ChannelSlack:
return &Slack{}, nil
case db.ChannelEmail:
return &Email{}, nil
case db.ChannelWebHook:
return &Webhook{}, nil
case db.ChannelTelegram:
return &Telegram{}, nil
default:
Expand All @@ -47,6 +47,81 @@ func GetPusher(typ int) (IPusher, error) {
// param alarm 警告的数据库连接
// param oneTheLogs 日志内容
func BuildAlarmMsg(notification db.Notification, table *db.BaseTable, alarm *db.Alarm, filter *db.AlarmFilter, partialLog string) (msg *db.PushMsg, err error) {
// groupKey := notification.GroupKey
var buffer bytes.Buffer
// base info
if notification.GetStatus() == db.AlarmStatusNormal {
buffer.WriteString("<font color=#008000>您的告警已恢复</font>\n")
} else {
buffer.WriteString("<font color=#FF0000>您有待处理的告警</font>\n")
}
buffer.WriteString(fmt.Sprintf("【告警名称】: %s\n", alarm.Name))
if alarm.Desc != "" {
buffer.WriteString(fmt.Sprintf("【告警描述】: %s\n", alarm.Desc))
}
users, phones := dutyOffices(alarm)
instance, _ := db.InstanceInfo(invoker.Db, table.Database.Iid)
statusText := "告警中"
for _, alert := range notification.Alerts {
end := alert.StartsAt.Add(time.Minute).Unix()
start := alert.StartsAt.Add(-alarm.GetInterval() - time.Minute).Unix()
buffer.WriteString(fmt.Sprintf("【触发时间】: %s\n", alert.StartsAt.Add(time.Hour*8).Format("2006-01-02 15:04:05")))
buffer.WriteString(fmt.Sprintf("【相关实例】: %s %s\n", instance.Name, instance.Desc))
buffer.WriteString(fmt.Sprintf("【日志库表】: %s %s\n", table.Name, table.Desc))
if notification.GetStatus() == db.AlarmStatusNormal {
statusText = "已恢复"
buffer.WriteString("【告警状态】: <font color=#008000>已恢复</font>\n")
} else {
buffer.WriteString("【告警状态】: <font color=red>告警中</font>\n")
}
dutyOfficesStr := ""
for _, u := range users {
if dutyOfficesStr == "" {
dutyOfficesStr = u.Nickname
} else {
dutyOfficesStr = fmt.Sprintf("%s/%s", dutyOfficesStr, u.Nickname)
}
}
if dutyOfficesStr != "" {
buffer.WriteString(fmt.Sprintf("【告警责任】: %s\n", dutyOfficesStr))
} else {
user, _ := db.UserInfo(alarm.Uid)
buffer.WriteString(fmt.Sprintf("【告警更新】: %s\n\n", user.Nickname))
}
jumpURL := fmt.Sprintf("%s/share?mode=0&tab=custom&tid=%d&kw=%s&start=%d&end=%d",
strings.TrimRight(econf.GetString("app.rootURL"), "/"), filter.Tid, url.QueryEscape(filter.When), start, end)
shortURL, err := shorturl.GenShortURL(jumpURL)
if err != nil {
elog.Error("shorturl.GenShortURL", elog.FieldErr(err), elog.String("jumpURL", jumpURL))
buffer.WriteString(fmt.Sprintf("【链接跳转】: %s\n", jumpURL))
} else {
buffer.WriteString(fmt.Sprintf("【链接跳转】: %s\n", shortURL))
}
if partialLog != "" {
partialLog = strings.Replace(partialLog, "\"", "", -1)
if len(partialLog) > 600 {
buffer.WriteString(fmt.Sprintf("【告警日志】: %s", partialLog[0:599]))
} else {
buffer.WriteString(fmt.Sprintf("【告警日志】: %s", partialLog))
}
}
}
pushMsg := &db.PushMsg{
Title: fmt.Sprintf("【%s】%s", statusText, alarm.Name),
Text: buffer.String(),
}
if len(phones) != 0 {
pushMsg.Mobiles = phones
}
return pushMsg, nil
}

// BuildAlarmMsgWithAt
// Description: 提供一个通用的md模式的获取内容的方法
// param notification 通知的部分方法
// param alarm 警告的数据库连接
// param oneTheLogs 日志内容
func BuildAlarmMsgWithAt(notification db.Notification, table *db.BaseTable, alarm *db.Alarm, filter *db.AlarmFilter, partialLog string) (msg *db.PushMsg, err error) {
// groupKey := notification.GroupKey
var buffer bytes.Buffer
// base info
Expand Down Expand Up @@ -75,18 +150,22 @@ func BuildAlarmMsg(notification db.Notification, table *db.BaseTable, alarm *db.
buffer.WriteString("【告警状态】: <font color=red>告警中</font>\n\n")
}
dutyOfficesStr := ""
for _, user := range users {
for _, u := range users {
at := u.Phone
if at == "" {
at = u.Nickname
}
if dutyOfficesStr == "" {
dutyOfficesStr = fmt.Sprintf("@%s", user.Phone)
dutyOfficesStr = fmt.Sprintf("@%s", at)
} else {
dutyOfficesStr = fmt.Sprintf("%s@%s", dutyOfficesStr, user.Phone)
dutyOfficesStr = fmt.Sprintf("%s@%s", dutyOfficesStr, at)
}
}
if dutyOfficesStr == "" {
user, _ := db.UserInfo(alarm.Uid)
buffer.WriteString(fmt.Sprintf("【告警创建】: %s\n\n", user.Nickname))
} else {
if dutyOfficesStr != "" {
buffer.WriteString(fmt.Sprintf("【告警责任】: %s\n\n", dutyOfficesStr))
} else {
user, _ := db.UserInfo(alarm.Uid)
buffer.WriteString(fmt.Sprintf("【告警更新】: %s\n\n", user.Nickname))
}
jumpURL := fmt.Sprintf("%s/share?mode=0&tab=custom&tid=%d&kw=%s&start=%d&end=%d",
strings.TrimRight(econf.GetString("app.rootURL"), "/"), filter.Tid, url.QueryEscape(filter.When), start, end,
Expand Down Expand Up @@ -117,7 +196,7 @@ func BuildAlarmMsg(notification db.Notification, table *db.BaseTable, alarm *db.
return pushMsg, nil
}

func Execute(channelIds []int, pushMsg *db.PushMsg) error {
func Execute(channelIds []int, pushMsg *db.PushMsg, pushMsgWithAt *db.PushMsg) error {
for _, channelId := range channelIds {
channel, err := db.AlarmChannelInfo(invoker.Db, channelId)
if err != nil {
Expand All @@ -127,7 +206,11 @@ func Execute(channelIds []int, pushMsg *db.PushMsg) error {
if err != nil {
return err
}
err = channelPusher.Send(&channel, pushMsg)
if channel.Typ == db.ChannelDingDing {
err = channelPusher.Send(&channel, pushMsgWithAt)
} else {
err = channelPusher.Send(&channel, pushMsg)
}
if err != nil {
return err
}
Expand Down
42 changes: 42 additions & 0 deletions api/internal/service/alarm/pusher/webhook.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package pusher

import (
"strings"

"github.com/go-resty/resty/v2"
"github.com/gotomicro/ego/core/elog"
"github.com/pkg/errors"

"github.com/clickvisual/clickvisual/api/pkg/model/db"
"github.com/clickvisual/clickvisual/api/pkg/model/dto"
)

type Webhook struct{}

func (e *Webhook) Send(channel *db.AlarmChannel, msg *db.PushMsg) (err error) {
elog.Info("webhookSend", elog.String("title", msg.Title), elog.Any("mobiles", msg.Mobiles))
client := resty.New()
resp, err := client.R().
SetHeader("Content-Type", "application/json").
SetBody(dto.WebhookReq{
CalledNumberList: msg.Mobiles,
CallContent: msg.Title + msg.Text,
}).
SetResult(&dto.WebhookResp{}). // or SetResult(AuthSuccess{}).
Post(channel.Key)
if err != nil {
return errors.New(err.Error())
}
if resp.StatusCode() != 200 {
webhookResp := resp.Result().(*dto.WebhookResp)
failedNumber := ""
for _, v := range webhookResp.Data {
if v.Message == "OK" {
continue
}
failedNumber += v.CalledNumber + ","
}
return errors.New("webhook send error, failed number: " + strings.TrimSuffix(failedNumber, ","))
}
return nil
}
16 changes: 12 additions & 4 deletions api/internal/service/alertmanager.go
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,20 @@ func (i *alert) HandlerAlertManager(alarmUUID string, filterIdStr string, notifi
}
// get partial log
partialLog := i.getPartialLog(op, &tableInfo, &alarm, filter)
pushMsg, errConstructMessage := pusher.BuildAlarmMsg(notification, &tableInfo, &alarm, filter, partialLog)
if errConstructMessage != nil {
elog.Error("PushAlertManagerError", elog.FieldErr(errConstructMessage), elog.Int("filterId", filterId), elog.String("alarmUUID", alarmUUID))

pushMsg, err := pusher.BuildAlarmMsg(notification, &tableInfo, &alarm, filter, partialLog)
if err != nil {
elog.Error("PushAlertManagerError", elog.FieldErr(err), elog.Int("filterId", filterId), elog.String("alarmUUID", alarmUUID))
return
}
if err = pusher.Execute(alarm.ChannelIds, pushMsg); err != nil {

pushMsgWithAt, err := pusher.BuildAlarmMsgWithAt(notification, &tableInfo, &alarm, filter, partialLog)
if err != nil {
elog.Error("PushAlertManagerError", elog.FieldErr(err), elog.Int("filterId", filterId), elog.String("alarmUUID", alarmUUID))
return
}

if err = pusher.Execute(alarm.ChannelIds, pushMsg, pushMsgWithAt); err != nil {
elog.Error("PushAlertManagerError", elog.FieldErr(err), elog.Int("filterId", filterId), elog.String("alarmUUID", alarmUUID))
_ = db.AlarmHistoryUpdate(invoker.Db, alarmHistory.ID, map[string]interface{}{"is_pushed": db.PushedStatusFail})
return
Expand Down
5 changes: 3 additions & 2 deletions api/internal/service/pandas/worker/crontab.go
Original file line number Diff line number Diff line change
Expand Up @@ -181,10 +181,11 @@ func buildCronFn(cr *db.BigdataCrontab) (err error) {
}

func pushExec(channelIds []int, text string, iid int) {
_ = pusher.Execute(channelIds, &db.PushMsg{
msg := db.PushMsg{
Title: "### <font color=#FF0000>您有待处理的告警</font>\n",
Text: fmt.Sprintf("Scheduled task execution failed: %s\n href: %s/bigdata?id=%d&navKey=TaskExecutionDetails\n",
text, strings.TrimRight(econf.GetString("app.rootURL"), "/"), iid,
),
})
}
_ = pusher.Execute(channelIds, &msg, &msg)
}
14 changes: 7 additions & 7 deletions api/pkg/model/db/alarmchannel.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ import (
)

const (
ChannelDingDing = iota + 1
ChannelWeChat
ChannelFeiShu
ChannelSlack
ChannelEmail
ChannelTelegram
ChannelDingDing = 1
ChannelWeChat = 2
ChannelFeiShu = 3
ChannelSlack = 4
ChannelWebHook = 5
ChannelTelegram = 6
)

const (
Expand Down Expand Up @@ -77,7 +77,7 @@ func (m *AlarmChannel) JudgmentType() (err error) {
case ChannelDingDing:
case ChannelWeChat:
case ChannelTelegram:
case ChannelEmail:
case ChannelWebHook:
case ChannelFeiShu:
if !(strings.HasPrefix(m.Key, LARKSUITE) || strings.HasPrefix(m.Key, FEISHUURL)) {
err = errors.New("invalid FeiShu webhook url")
Expand Down

0 comments on commit f667009

Please sign in to comment.