nightingale1/models/alert_event.go

259 lines
7.8 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package models
import (
"encoding/json"
"fmt"
"strings"
"github.com/didi/nightingale/v5/vos"
"github.com/toolkits/pkg/logger"
"github.com/toolkits/pkg/str"
"xorm.io/builder"
)
type AlertEvent struct {
Id int64 `json:"id"`
RuleId int64 `json:"rule_id"`
RuleName string `json:"rule_name"`
RuleNote string `json:"rule_note"`
HashId string `json:"hash_id"` // 唯一标识
IsPromePull int `json:"is_prome_pull"` // 代表是否是prometheus pull告警为1时前端使用 ReadableExpression 拉取最近1小时数据
LastSend bool `json:"last_sent" xorm:"-"` // true 代表上次发了false代表还没发:给prometheus做for判断的
AlertDuration int64 `xorm:"-" json:"alert_duration"` // 告警统计周期PULL模型会当做P8S的for时间
ResClasspaths string `json:"res_classpaths"`
ResIdent string `json:"res_ident" xorm:"-"` // res_ident会出现在tags字段就不用单独写入数据库了但是各块逻辑中有个单独的res_ident字段更便于处理所以struct里还留有这个字段前端不用展示这个字段
Priority int `json:"priority"`
Status int `json:"status"` // 标识是否 被屏蔽
IsRecovery int `json:"is_recovery" xorm:"-"` // 0: alert, 1: recovery
HistoryPoints json.RawMessage `json:"history_points"` // HistoryPoints{}
TriggerTime int64 `json:"trigger_time"`
Values string `json:"values" xorm:"-"` // e.g. cpu.idle: 23.3; load.1min: 32
NotifyChannels string `json:"notify_channels"`
NotifyGroups string `json:"notify_groups"`
NotifyUsers string `json:"notify_users"`
RunbookUrl string `json:"runbook_url"`
ReadableExpression string `json:"readable_expression"` // e.g. mem.bytes.used.percent(all,60s) > 0
Tags string `json:"tags"` // merge data_tags rule_tags and res_tags
NotifyGroupObjs []UserGroup `json:"notify_group_objs" xorm:"-"`
NotifyUserObjs []User `json:"notify_user_objs" xorm:"-"`
TagMap map[string]string `json:"tag_map" xorm:"-"`
}
// IsAlert 语法糖避免直接拿IsRecovery字段做比对不直观易出错
func (ae *AlertEvent) IsAlert() bool {
return ae.IsRecovery != 1
}
// IsRecov 语法糖避免直接拿IsRecovery字段做比对不直观易出错
func (ae *AlertEvent) IsRecov() bool {
return ae.IsRecovery == 1
}
// MarkAlert 语法糖,标记为告警状态
func (ae *AlertEvent) MarkAlert() {
ae.IsRecovery = 0
}
// MarkRecov 语法糖,标记为恢复状态
func (ae *AlertEvent) MarkRecov() {
ae.IsRecovery = 1
}
// MarkMuted 语法糖,标记为屏蔽状态
func (ae *AlertEvent) MarkMuted() {
ae.Status = 1
}
func (ae *AlertEvent) String() string {
return fmt.Sprintf("id:%d,rule_id:%d,rule_name:%s,rule_note:%s,hash_id:%s,is_prome_pull:%d,alert_duration:%d,res_classpaths:%s,res_ident:%s,priority:%d,status:%d,is_recovery:%d,history_points:%s,trigger_time:%d,values:%s,notify_channels:%s,runbook_url:%s,readable_expression:%s,tags:%s,notify_group_objs:%+v,notify_user_objs:%+v,tag_map:%v",
ae.Id,
ae.RuleId,
ae.RuleName,
ae.RuleNote,
ae.HashId,
ae.IsPromePull,
ae.AlertDuration,
ae.ResClasspaths,
ae.ResIdent,
ae.Priority,
ae.Status,
ae.IsRecovery,
string(ae.HistoryPoints),
ae.TriggerTime,
ae.Values,
ae.NotifyChannels,
ae.RunbookUrl,
ae.ReadableExpression,
ae.Tags,
ae.NotifyGroupObjs,
ae.NotifyUserObjs,
ae.TagMap)
}
func (ae *AlertEvent) TableName() string {
return "alert_event"
}
func (ae *AlertEvent) FillObjs() error {
userGroupIds := strings.Fields(ae.NotifyGroups)
if len(userGroupIds) > 0 {
groups, err := UserGroupGetsByIdsStr(userGroupIds)
if err != nil {
return err
}
ae.NotifyGroupObjs = groups
}
userIds := strings.Fields(ae.NotifyUsers)
if len(userIds) > 0 {
users, err := UserGetsByIdsStr(userIds)
if err != nil {
return err
}
ae.NotifyUserObjs = users
}
return nil
}
func (ae *AlertEvent) GetHistoryPoints() ([]vos.HistoryPoints, error) {
historyPoints := []vos.HistoryPoints{}
err := json.Unmarshal([]byte(ae.HistoryPoints), &historyPoints)
return historyPoints, err
}
func (ae *AlertEvent) Add() error {
return DBInsertOne(ae)
}
func (ar *AlertEvent) DelByHashId() error {
_, err := DB.Where("hash_id=?", ar.HashId).Delete(new(AlertEvent))
if err != nil {
logger.Errorf("mysql.error: delete alert_event fail: %v", err)
return internalServerError
}
return nil
}
func (ar *AlertEvent) HashIdExists() (bool, error) {
num, err := DB.Where("hash_id=?", ar.HashId).Count(new(AlertEvent))
return num > 0, err
}
func (ar *AlertEvent) Del() error {
_, err := DB.Where("id=?", ar.Id).Delete(new(AlertEvent))
if err != nil {
logger.Errorf("mysql.error: delete alert_event fail: %v", err)
return internalServerError
}
return nil
}
func AlertEventsDel(ids []int64) error {
if len(ids) == 0 {
return fmt.Errorf("param ids is empty")
}
_, err := DB.Exec("DELETE FROM alert_event where id in (" + str.IdsString(ids) + ")")
if err != nil {
logger.Errorf("mysql.error: delete alert_event(%v) fail: %v", ids, err)
return internalServerError
}
return nil
}
func AlertEventTotal(stime, etime int64, query string, status, priority int) (num int64, err error) {
cond := builder.NewCond()
if stime != 0 && etime != 0 {
cond = cond.And(builder.Between{Col: "trigger_time", LessVal: stime, MoreVal: etime})
}
if status != -1 {
cond = cond.And(builder.Eq{"status": status})
}
if priority != -1 {
cond = cond.And(builder.Eq{"priority": priority})
}
if query != "" {
arr := strings.Fields(query)
for i := 0; i < len(arr); i++ {
qarg := "%" + arr[i] + "%"
innerCond := builder.NewCond()
innerCond = innerCond.Or(builder.Like{"res_classpaths", qarg})
innerCond = innerCond.Or(builder.Like{"rule_name", qarg})
innerCond = innerCond.Or(builder.Like{"tags", qarg})
cond = cond.And(innerCond)
}
}
num, err = DB.Where(cond).Count(new(AlertEvent))
if err != nil {
logger.Errorf("mysql.error: count alert_event fail: %v", err)
return 0, internalServerError
}
return num, nil
}
func AlertEventGets(stime, etime int64, query string, status, priority int, limit, offset int) ([]AlertEvent, error) {
cond := builder.NewCond()
if stime != 0 && etime != 0 {
cond = cond.And(builder.Between{Col: "trigger_time", LessVal: stime, MoreVal: etime})
}
if status != -1 {
cond = cond.And(builder.Eq{"status": status})
}
if priority != -1 {
cond = cond.And(builder.Eq{"priority": priority})
}
if query != "" {
arr := strings.Fields(query)
for i := 0; i < len(arr); i++ {
qarg := "%" + arr[i] + "%"
innerCond := builder.NewCond()
innerCond = innerCond.Or(builder.Like{"res_classpaths", qarg})
innerCond = innerCond.Or(builder.Like{"rule_name", qarg})
innerCond = innerCond.Or(builder.Like{"tags", qarg})
cond = cond.And(innerCond)
}
}
var objs []AlertEvent
err := DB.Where(cond).Desc("trigger_time").Limit(limit, offset).Find(&objs)
if err != nil {
logger.Errorf("mysql.error: query alert_event fail: %v", err)
return objs, internalServerError
}
if len(objs) == 0 {
return []AlertEvent{}, nil
}
return objs, nil
}
func AlertEventGet(where string, args ...interface{}) (*AlertEvent, error) {
var obj AlertEvent
has, err := DB.Where(where, args...).Get(&obj)
if err != nil {
logger.Errorf("mysql.error: query alert_event(%s)%+v fail: %s", where, args, err)
return nil, internalServerError
}
if !has {
return nil, nil
}
return &obj, nil
}