Merge pull request 'alerting optmize' (#136) from alerting_optmize into master

This commit is contained in:
silenceqi 2023-08-28 19:46:52 +08:00
commit df477c1485
13 changed files with 855 additions and 60 deletions

View File

@ -133,6 +133,7 @@ func main() {
orm.RegisterSchemaWithIndexName(alerting.Channel{}, "channel") orm.RegisterSchemaWithIndexName(alerting.Channel{}, "channel")
orm.RegisterSchemaWithIndexName(insight.Visualization{}, "visualization") orm.RegisterSchemaWithIndexName(insight.Visualization{}, "visualization")
orm.RegisterSchemaWithIndexName(insight.Dashboard{}, "dashboard") orm.RegisterSchemaWithIndexName(insight.Dashboard{}, "dashboard")
orm.RegisterSchemaWithIndexName(insight.Widget{}, "widget")
orm.RegisterSchemaWithIndexName(task1.Task{}, "task") orm.RegisterSchemaWithIndexName(task1.Task{}, "task")
orm.RegisterSchemaWithIndexName(model.Layout{}, "layout") orm.RegisterSchemaWithIndexName(model.Layout{}, "layout")
orm.RegisterSchemaWithIndexName(model.Notification{}, "notification") orm.RegisterSchemaWithIndexName(model.Notification{}, "notification")

View File

@ -22,7 +22,9 @@ type Alert struct {
Title string `json:"title" elastic_mapping:"title: { type: keyword }"` Title string `json:"title" elastic_mapping:"title: { type: keyword }"`
Message string `json:"message" elastic_mapping:"context: { type: keyword, copy_to:search_text }"` Message string `json:"message" elastic_mapping:"context: { type: keyword, copy_to:search_text }"`
AcknowledgedTime interface{} `json:"acknowledged_time,omitempty"` AcknowledgedTime interface{} `json:"acknowledged_time,omitempty"`
ActionExecutionResults []ActionExecutionResult `json:"action_execution_results"` ActionExecutionResults []ActionExecutionResult `json:"action_execution_results,omitempty"`
RecoverActionResults []ActionExecutionResult `json:"recover_action_results,omitempty"`
EscalationActionResults []ActionExecutionResult `json:"escalation_action_results,omitempty"`
Users []string `json:"users,omitempty"` Users []string `json:"users,omitempty"`
State string `json:"state"` State string `json:"state"`
Error string `json:"error,omitempty"` Error string `json:"error,omitempty"`
@ -40,6 +42,7 @@ type ActionExecutionResult struct {
Message string `json:"message"` Message string `json:"message"`
ChannelName string `json:"channel_name"` ChannelName string `json:"channel_name"`
ChannelType string `json:"channel_type"` ChannelType string `json:"channel_type"`
ChannelID string `json:"channel_id"`
} }
const ( const (
@ -70,6 +73,8 @@ type AlertMessage struct {
IgnoredUser string `json:"ignored_user,omitempty" elastic_mapping:"ignored_user: { type: keyword,copy_to:search_text }"` IgnoredUser string `json:"ignored_user,omitempty" elastic_mapping:"ignored_user: { type: keyword,copy_to:search_text }"`
Priority string `json:"priority" elastic_mapping:"priority: { type: keyword }"` Priority string `json:"priority" elastic_mapping:"priority: { type: keyword }"`
SearchText string `json:"-" elastic_mapping:"search_text:{type:text,index_prefixes:{},index_phrases:true, analyzer:suggest_text_search }"` SearchText string `json:"-" elastic_mapping:"search_text:{type:text,index_prefixes:{},index_phrases:true, analyzer:suggest_text_search }"`
Category string `json:"category,omitempty" elastic_mapping:"category: { type: keyword,copy_to:search_text }"`
Tags []string `json:"tags,omitempty" elastic_mapping:"tags: { type: keyword,copy_to:search_text }"`
} }
/* /*

View File

@ -31,6 +31,8 @@ type Rule struct {
Name string `json:"name" elastic_mapping:"name: { type: keyword }"` Name string `json:"name" elastic_mapping:"name: { type: keyword }"`
Id string `json:"id" elastic_mapping:"id: { type: keyword }"` Id string `json:"id" elastic_mapping:"id: { type: keyword }"`
} `json:"creator" elastic_mapping:"creator:{type:object}"` } `json:"creator" elastic_mapping:"creator:{type:object}"`
Category string `json:"category,omitempty" elastic_mapping:"category: { type: keyword,copy_to:search_text }"`
Tags []string `json:"tags,omitempty" elastic_mapping:"tags: { type: keyword,copy_to:search_text }"`
} }
func (rule *Rule) GetOrInitExpression() (string, error){ func (rule *Rule) GetOrInitExpression() (string, error){

13
model/insight/widget.go Normal file
View File

@ -0,0 +1,13 @@
/* Copyright © INFINI Ltd. All rights reserved.
* Web: https://infinilabs.com
* Email: hello#infini.ltd */
package insight
import "infini.sh/framework/core/orm"
type Widget struct {
orm.ORMObjectBase
Title string `json:"title" elastic_mapping:"title: { type: text }"`
Config interface{}`json:"config" elastic_mapping:"config: { type: object,enabled:false }"`
}

View File

@ -19,6 +19,7 @@ func (alert *AlertAPI) Init() {
api.HandleAPIMethod(api.POST, "/alerting/rule", alert.RequirePermission(alert.createRule, enum.PermissionAlertRuleWrite)) api.HandleAPIMethod(api.POST, "/alerting/rule", alert.RequirePermission(alert.createRule, enum.PermissionAlertRuleWrite))
api.HandleAPIMethod(api.POST, "/alerting/rule/test", alert.RequireLogin(alert.sendTestMessage)) api.HandleAPIMethod(api.POST, "/alerting/rule/test", alert.RequireLogin(alert.sendTestMessage))
api.HandleAPIMethod(api.DELETE, "/alerting/rule/:rule_id", alert.RequirePermission(alert.deleteRule, enum.PermissionAlertRuleWrite)) api.HandleAPIMethod(api.DELETE, "/alerting/rule/:rule_id", alert.RequirePermission(alert.deleteRule, enum.PermissionAlertRuleWrite))
api.HandleAPIMethod(api.DELETE, "/alerting/rule", alert.RequirePermission(alert.batchDeleteRule, enum.PermissionAlertRuleWrite))
api.HandleAPIMethod(api.PUT, "/alerting/rule/:rule_id", alert.RequirePermission(alert.updateRule, enum.PermissionAlertRuleWrite)) api.HandleAPIMethod(api.PUT, "/alerting/rule/:rule_id", alert.RequirePermission(alert.updateRule, enum.PermissionAlertRuleWrite))
api.HandleAPIMethod(api.GET, "/alerting/rule/_search", alert.RequirePermission(alert.searchRule, enum.PermissionAlertRuleRead)) api.HandleAPIMethod(api.GET, "/alerting/rule/_search", alert.RequirePermission(alert.searchRule, enum.PermissionAlertRuleRead))
api.HandleAPIMethod(api.GET, "/alerting/stats", alert.RequirePermission(alert.getAlertStats, enum.PermissionAlertHistoryRead)) api.HandleAPIMethod(api.GET, "/alerting/stats", alert.RequirePermission(alert.getAlertStats, enum.PermissionAlertHistoryRead))
@ -27,6 +28,9 @@ func (alert *AlertAPI) Init() {
api.HandleAPIMethod(api.POST, "/alerting/rule/:rule_id/_enable", alert.RequirePermission(alert.enableRule, enum.PermissionAlertRuleWrite)) api.HandleAPIMethod(api.POST, "/alerting/rule/:rule_id/_enable", alert.RequirePermission(alert.enableRule, enum.PermissionAlertRuleWrite))
api.HandleAPIMethod(api.GET, "/alerting/rule/:rule_id/metric", alert.RequirePermission(alert.getMetricData, enum.PermissionAlertRuleRead)) api.HandleAPIMethod(api.GET, "/alerting/rule/:rule_id/metric", alert.RequirePermission(alert.getMetricData, enum.PermissionAlertRuleRead))
api.HandleAPIMethod(api.GET, "/alerting/rule/:rule_id/info", alert.RequirePermission(alert.getRuleDetail, enum.PermissionAlertRuleRead, enum.PermissionAlertMessageRead)) api.HandleAPIMethod(api.GET, "/alerting/rule/:rule_id/info", alert.RequirePermission(alert.getRuleDetail, enum.PermissionAlertRuleRead, enum.PermissionAlertMessageRead))
api.HandleAPIMethod(api.POST, "/alerting/rule/_enable", alert.RequirePermission(alert.batchEnableRule, enum.PermissionAlertRuleWrite))
api.HandleAPIMethod(api.POST, "/alerting/rule/_disable", alert.RequirePermission(alert.batchDisableRule, enum.PermissionAlertRuleWrite))
api.HandleAPIMethod(api.GET, "/alerting/rule/_search_values", alert.RequirePermission(alert.searchFieldValues, enum.PermissionAlertRuleRead))
api.HandleAPIMethod(api.GET, "/alerting/channel/:channel_id", alert.RequirePermission(alert.getChannel, enum.PermissionAlertChannelRead)) api.HandleAPIMethod(api.GET, "/alerting/channel/:channel_id", alert.RequirePermission(alert.getChannel, enum.PermissionAlertChannelRead))
api.HandleAPIMethod(api.POST, "/alerting/channel", alert.RequirePermission(alert.createChannel, enum.PermissionAlertChannelWrite)) api.HandleAPIMethod(api.POST, "/alerting/channel", alert.RequirePermission(alert.createChannel, enum.PermissionAlertChannelWrite))
@ -34,6 +38,8 @@ func (alert *AlertAPI) Init() {
api.HandleAPIMethod(api.PUT, "/alerting/channel/:channel_id", alert.RequirePermission(alert.updateChannel, enum.PermissionAlertChannelWrite)) api.HandleAPIMethod(api.PUT, "/alerting/channel/:channel_id", alert.RequirePermission(alert.updateChannel, enum.PermissionAlertChannelWrite))
api.HandleAPIMethod(api.GET, "/alerting/channel/_search", alert.RequirePermission(alert.searchChannel, enum.PermissionAlertChannelRead)) api.HandleAPIMethod(api.GET, "/alerting/channel/_search", alert.RequirePermission(alert.searchChannel, enum.PermissionAlertChannelRead))
api.HandleAPIMethod(api.POST, "/alerting/channel/test", alert.RequirePermission(alert.testChannel, enum.PermissionAlertChannelWrite)) api.HandleAPIMethod(api.POST, "/alerting/channel/test", alert.RequirePermission(alert.testChannel, enum.PermissionAlertChannelWrite))
api.HandleAPIMethod(api.POST, "/alerting/channel/_enable", alert.RequirePermission(alert.batchEnableChannel, enum.PermissionAlertChannelWrite))
api.HandleAPIMethod(api.POST, "/alerting/channel/_disable", alert.RequirePermission(alert.batchDisableChannel, enum.PermissionAlertChannelWrite))
api.HandleAPIMethod(api.GET, "/alerting/alert/_search", alert.RequirePermission(alert.searchAlert, enum.PermissionAlertHistoryRead)) api.HandleAPIMethod(api.GET, "/alerting/alert/_search", alert.RequirePermission(alert.searchAlert, enum.PermissionAlertHistoryRead))
api.HandleAPIMethod(api.GET, "/alerting/alert/:alert_id", alert.RequirePermission(alert.getAlert, enum.PermissionAlertHistoryRead)) api.HandleAPIMethod(api.GET, "/alerting/alert/:alert_id", alert.RequirePermission(alert.getAlert, enum.PermissionAlertHistoryRead))
@ -43,6 +49,7 @@ func (alert *AlertAPI) Init() {
api.HandleAPIMethod(api.POST, "/alerting/message/_ignore", alert.RequirePermission(alert.ignoreAlertMessage, enum.PermissionAlertMessageWrite)) api.HandleAPIMethod(api.POST, "/alerting/message/_ignore", alert.RequirePermission(alert.ignoreAlertMessage, enum.PermissionAlertMessageWrite))
api.HandleAPIMethod(api.GET, "/alerting/message/_stats", alert.RequirePermission(alert.getAlertMessageStats, enum.PermissionAlertMessageRead)) api.HandleAPIMethod(api.GET, "/alerting/message/_stats", alert.RequirePermission(alert.getAlertMessageStats, enum.PermissionAlertMessageRead))
api.HandleAPIMethod(api.GET, "/alerting/message/:message_id", alert.RequirePermission(alert.getAlertMessage, enum.PermissionAlertMessageRead)) api.HandleAPIMethod(api.GET, "/alerting/message/:message_id", alert.RequirePermission(alert.getAlertMessage, enum.PermissionAlertMessageRead))
api.HandleAPIMethod(api.GET, "/alerting/message/:message_id/notification", alert.getMessageNotificationInfo)
//just for test //just for test

View File

@ -315,4 +315,65 @@ func (h *AlertAPI) testChannel(w http.ResponseWriter, req *http.Request, ps http
return return
} }
h.WriteAckOKJSON(w) h.WriteAckOKJSON(w)
}
func (alertAPI *AlertAPI) batchEnableChannel(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
var channelIDs = []string{}
err := alertAPI.DecodeJSON(req, &channelIDs)
if err != nil {
log.Error(err)
alertAPI.WriteError(w, err.Error(), http.StatusInternalServerError)
return
}
if len(channelIDs) == 0 {
alertAPI.WriteJSON(w, util.MapStr{}, http.StatusOK)
return
}
if len(channelIDs) > 0 {
err = setChannelEnabled(true, channelIDs)
if err != nil {
log.Error(err)
alertAPI.WriteError(w, err.Error(), http.StatusInternalServerError)
return
}
}
alertAPI.WriteAckOKJSON(w)
}
func (alertAPI *AlertAPI) batchDisableChannel(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
var channelIDs = []string{}
err := alertAPI.DecodeJSON(req, &channelIDs)
if err != nil {
log.Error(err)
alertAPI.WriteError(w, err.Error(), http.StatusInternalServerError)
return
}
if len(channelIDs) == 0 {
alertAPI.WriteJSON(w, util.MapStr{}, http.StatusOK)
return
}
if len(channelIDs) > 0 {
err = setChannelEnabled(false, channelIDs)
if err != nil {
log.Error(err)
alertAPI.WriteError(w, err.Error(), http.StatusInternalServerError)
return
}
}
alertAPI.WriteAckOKJSON(w)
}
func setChannelEnabled(enabled bool, channelIDs []string) error {
q := util.MapStr{
"query": util.MapStr{
"terms": util.MapStr{
"id": channelIDs,
},
},
"script": util.MapStr{
"source": fmt.Sprintf("ctx._source['enabled'] = %v", enabled),
},
}
err := orm.UpdateBy(alerting.Channel{}, util.MustToJSONBytes(q))
return err
} }

View File

@ -6,6 +6,7 @@ package alerting
import ( import (
"fmt" "fmt"
"github.com/buger/jsonparser"
log "github.com/cihub/seelog" log "github.com/cihub/seelog"
"infini.sh/console/model/alerting" "infini.sh/console/model/alerting"
alerting2 "infini.sh/console/service/alerting" alerting2 "infini.sh/console/service/alerting"
@ -25,6 +26,7 @@ func (h *AlertAPI) ignoreAlertMessage(w http.ResponseWriter, req *http.Request,
body := struct { body := struct {
Messages []alerting.AlertMessage `json:"messages"` Messages []alerting.AlertMessage `json:"messages"`
IgnoredReason string `json:"ignored_reason"` IgnoredReason string `json:"ignored_reason"`
IsReset bool `json:"is_reset"`
}{} }{}
err := h.DecodeJSON(req, &body) err := h.DecodeJSON(req, &body)
if err != nil { if err != nil {
@ -41,27 +43,41 @@ func (h *AlertAPI) ignoreAlertMessage(w http.ResponseWriter, req *http.Request,
messageIDs = append(messageIDs, msg.ID) messageIDs = append(messageIDs, msg.ID)
} }
currentUser := h.GetCurrentUser(req) currentUser := h.GetCurrentUser(req)
must := []util.MapStr{
{
"terms": util.MapStr{
"_id": messageIDs,
},
},
}
var source string
if body.IsReset {
must = append(must, util.MapStr{
"term": util.MapStr{
"status": util.MapStr{
"value": alerting.MessageStateIgnored,
},
},
})
source = fmt.Sprintf("ctx._source['status'] = '%s'", alerting.MessageStateAlerting)
}else {
must = append(must, util.MapStr{
"term": util.MapStr{
"status": util.MapStr{
"value": alerting.MessageStateAlerting,
},
},
})
source = fmt.Sprintf("ctx._source['status'] = '%s';ctx._source['ignored_time']='%s';ctx._source['ignored_reason']='%s';ctx._source['ignored_user']='%s'", alerting.MessageStateIgnored, time.Now().Format(time.RFC3339Nano), body.IgnoredReason, currentUser)
}
queryDsl := util.MapStr{ queryDsl := util.MapStr{
"query": util.MapStr{ "query": util.MapStr{
"bool": util.MapStr{ "bool": util.MapStr{
"must": []util.MapStr{ "must": must,
{
"terms": util.MapStr{
"_id": messageIDs,
},
},
{
"term": util.MapStr{
"status": util.MapStr{
"value": alerting.MessageStateAlerting,
},
},
},
},
}, },
}, },
"script": util.MapStr{ "script": util.MapStr{
"source": fmt.Sprintf("ctx._source['status'] = '%s';ctx._source['ignored_time']='%s';ctx._source['ignored_reason']='%s';ctx._source['ignored_user']='%s'", alerting.MessageStateIgnored, time.Now().Format(time.RFC3339Nano), body.IgnoredReason, currentUser), "source": source,
}, },
} }
err = orm.UpdateBy(alerting.AlertMessage{}, util.MustToJSONBytes(queryDsl)) err = orm.UpdateBy(alerting.AlertMessage{}, util.MustToJSONBytes(queryDsl))
@ -160,10 +176,53 @@ func (h *AlertAPI) getAlertMessageStats(w http.ResponseWriter, req *http.Request
return return
} }
statusCounts[alerting.MessageStateIgnored] = countRes.Count statusCounts[alerting.MessageStateIgnored] = countRes.Count
queryDsl = util.MapStr{
"size": 0,
"aggs": util.MapStr{
"terms_by_category": util.MapStr{
"terms": util.MapStr{
"field": "category",
"size": 100,
},
},
"terms_by_tags": util.MapStr{
"terms": util.MapStr{
"field": "tags",
"size": 100,
},
},
},
}
searchRes, err = esClient.SearchWithRawQueryDSL(indexName, util.MustToJSONBytes(queryDsl) )
if err != nil {
h.WriteJSON(w, util.MapStr{
"error": err.Error(),
}, http.StatusInternalServerError)
return
}
categories := []string{}
if termsAgg, ok := searchRes.Aggregations["terms_by_category"]; ok {
for _, bk := range termsAgg.Buckets {
if cate, ok := bk["key"].(string); ok {
categories = append(categories, cate)
}
}
}
tags := []string{}
if termsAgg, ok := searchRes.Aggregations["terms_by_tags"]; ok {
for _, bk := range termsAgg.Buckets {
if tag, ok := bk["key"].(string); ok {
tags = append(tags, tag)
}
}
}
h.WriteJSON(w, util.MapStr{ h.WriteJSON(w, util.MapStr{
"alert": util.MapStr{ "alert": util.MapStr{
"current": statusCounts, "current": statusCounts,
}, },
"categories": categories,
"tags": tags,
}, http.StatusOK) }, http.StatusOK)
} }
@ -182,6 +241,8 @@ func (h *AlertAPI) searchAlertMessage(w http.ResponseWriter, req *http.Request,
max = h.GetParameterOrDefault(req, "max", "now") max = h.GetParameterOrDefault(req, "max", "now")
mustBuilder = &strings.Builder{} mustBuilder = &strings.Builder{}
sortBuilder = strings.Builder{} sortBuilder = strings.Builder{}
category = h.GetParameterOrDefault(req, "category", "")
tags = h.GetParameterOrDefault(req, "tags", "")
) )
mustBuilder.WriteString(fmt.Sprintf(`{"range":{"created":{"gte":"%s", "lte": "%s"}}}`, min, max)) mustBuilder.WriteString(fmt.Sprintf(`{"range":{"created":{"gte":"%s", "lte": "%s"}}}`, min, max))
if ruleID != "" { if ruleID != "" {
@ -213,6 +274,14 @@ func (h *AlertAPI) searchAlertMessage(w http.ResponseWriter, req *http.Request,
mustBuilder.WriteString(",") mustBuilder.WriteString(",")
mustBuilder.WriteString(fmt.Sprintf(`{"term":{"priority":{"value":"%s"}}}`, priority)) mustBuilder.WriteString(fmt.Sprintf(`{"term":{"priority":{"value":"%s"}}}`, priority))
} }
if category != "" {
mustBuilder.WriteString(",")
mustBuilder.WriteString(fmt.Sprintf(`{"term":{"category":{"value":"%s"}}}`, category))
}
if tags != "" {
mustBuilder.WriteString(",")
mustBuilder.WriteString(fmt.Sprintf(`{"term":{"tags":{"value":"%s"}}}`, tags))
}
size, _ := strconv.Atoi(strSize) size, _ := strconv.Atoi(strSize)
if size <= 0 { if size <= 0 {
size = 20 size = 20
@ -286,8 +355,12 @@ func (h *AlertAPI) getAlertMessage(w http.ResponseWriter, req *http.Request, ps
return return
} }
metricExpression, _ := rule.Metrics.GenerateExpression() metricExpression, _ := rule.Metrics.GenerateExpression()
var hitCondition string
for i, cond := range rule.Conditions.Items { for i, cond := range rule.Conditions.Items {
expression, _ := cond.GenerateConditionExpression() expression, _ := cond.GenerateConditionExpression()
if cond.Priority == message.Priority {
hitCondition = strings.ReplaceAll(expression, "result", "")
}
rule.Conditions.Items[i].Expression = strings.ReplaceAll(expression, "result", metricExpression) rule.Conditions.Items[i].Expression = strings.ReplaceAll(expression, "result", metricExpression)
} }
var duration time.Duration var duration time.Duration
@ -315,6 +388,202 @@ func (h *AlertAPI) getAlertMessage(w http.ResponseWriter, req *http.Request, ps
"ignored_reason": message.IgnoredReason, "ignored_reason": message.IgnoredReason,
"ignored_user": message.IgnoredUser, "ignored_user": message.IgnoredUser,
"status": message.Status, "status": message.Status,
"expression": rule.Metrics.Expression,
"hit_condition": hitCondition,
} }
h.WriteJSON(w, detailObj, http.StatusOK) h.WriteJSON(w, detailObj, http.StatusOK)
}
func (h *AlertAPI) getMessageNotificationInfo(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
message := &alerting.AlertMessage{
ID: ps.ByName("message_id"),
}
exists, err := orm.Get(message)
if !exists || err != nil {
log.Error(err)
h.WriteJSON(w, util.MapStr{
"_id": message.ID,
"found": false,
}, http.StatusNotFound)
return
}
rule := &alerting.Rule{
ID: message.RuleID,
}
exists, err = orm.Get(rule)
if !exists || err != nil {
log.Error(err)
h.WriteError(w, fmt.Sprintf("rule [%s] not found", rule.ID), http.StatusInternalServerError)
return
}
notificationInfo := util.MapStr{}
if rule.NotificationConfig == nil && rule.RecoveryNotificationConfig == nil {
notificationInfo["is_empty"] = true
h.WriteJSON(w, notificationInfo, http.StatusOK)
return
}
stats, err := getMessageNotificationStats(message)
if err != nil {
log.Error(err)
h.WriteError(w, err.Error(), http.StatusInternalServerError)
return
}
if rule.NotificationConfig != nil {
notificationInfo["alerting"] = util.MapStr{
"accept_time_range": rule.NotificationConfig.AcceptTimeRange,
"throttle_period": rule.NotificationConfig.ThrottlePeriod,
"escalation_enabled": rule.NotificationConfig.EscalationEnabled,
"escalation_throttle_period": rule.NotificationConfig.EscalationThrottlePeriod,
"normal_stats": stats["normal"],
"escalation_stats": stats["escalation"],
}
}
if rule.RecoveryNotificationConfig != nil {
notificationInfo["recovery"] = util.MapStr{
"stats": stats["recovery"],
}
}
h.WriteJSON(w, notificationInfo, http.StatusOK)
}
func getMessageNotificationStats(msg *alerting.AlertMessage )(util.MapStr, error){
rangeQ := util.MapStr{
"gte": msg.Created.UnixMilli(),
}
if msg.Status == alerting.MessageStateRecovered {
rangeQ["lte"] = msg.Updated.UnixMilli()
}
aggs := util.MapStr{
"grp_normal_channel": util.MapStr{
"terms": util.MapStr{
"field": "action_execution_results.channel_type",
"size": 20,
},
"aggs": util.MapStr{
"top": util.MapStr{
"top_hits": util.MapStr{
"sort": []util.MapStr{
{
"created": util.MapStr{
"order": "desc",
},
},
},
"_source": util.MapStr{
"includes": []string{"created", "action_execution_results.channel_name", "action_execution_results.channel_type"},
},
"size": 1,
},
},
},
},
"grp_escalation_channel": util.MapStr{
"terms": util.MapStr{
"field": "escalation_action_results.channel_type",
"size": 20,
},
"aggs": util.MapStr{
"top": util.MapStr{
"top_hits": util.MapStr{
"sort": []util.MapStr{
{
"created": util.MapStr{
"order": "desc",
},
},
},
"_source": util.MapStr{
"includes": []string{"created", "escalation_action_results.channel_name", "escalation_action_results.channel_type"},
},
"size": 1,
},
},
},
},
}
if msg.Status == alerting.MessageStateRecovered {
aggs["grp_recover_channel"] = util.MapStr{
"terms": util.MapStr{
"field": "recover_action_results.channel_type",
"size": 20,
},
"aggs": util.MapStr{
"top": util.MapStr{
"top_hits": util.MapStr{
"sort": []util.MapStr{
{
"created": util.MapStr{
"order": "desc",
},
},
},
"_source": util.MapStr{
"includes": []string{"created", "recover_action_results.channel_name", "recover_action_results.channel_type"},
},
"size": 1,
},
},
},
}
}
query := util.MapStr{
"size": 0,
"aggs": aggs,
"query": util.MapStr{
"bool": util.MapStr{
"must": []util.MapStr{
{
"range": util.MapStr{
"created": rangeQ,
},
},
{
"term": util.MapStr{
"rule_id": util.MapStr{
"value": msg.RuleID,
},
},
},
},
},
},
}
q := orm.Query{
RawQuery: util.MustToJSONBytes(query),
}
err, result := orm.Search(alerting.Alert{}, &q)
if err != nil {
return nil, err
}
var normalStats = extractStatsFromRaw(result.Raw, "grp_normal_channel", "action_execution_results")
var escalationStats = extractStatsFromRaw(result.Raw, "grp_escalation_channel", "escalation_action_results")
stats := util.MapStr{
"normal": normalStats,
"escalation": escalationStats,
}
if msg.Status == alerting.MessageStateRecovered {
recoverStats := extractStatsFromRaw(result.Raw, "grp_recover_channel", "recover_action_results")
stats["recovery"] = recoverStats
}
return stats, nil
}
func extractStatsFromRaw(searchRawRes []byte, grpKey string, actionKey string) []util.MapStr {
var stats []util.MapStr
jsonparser.ArrayEach(searchRawRes, func(value []byte, dataType jsonparser.ValueType, offset int, err error) {
statsItem := util.MapStr{}
statsItem["channel_type"], _ = jsonparser.GetString(value, "key")
statsItem["count"], _ = jsonparser.GetInt(value, "doc_count")
jsonparser.ArrayEach(value, func(v []byte, dataType jsonparser.ValueType, offset int, err error) {
ck, _ := jsonparser.GetString(v, "channel_type")
cn, _ := jsonparser.GetString(v, "channel_name")
if ck == statsItem["channel_type"] {
statsItem["channel_name"] = cn
}
}, "top", "hits","hits", "[0]", "_source",actionKey)
statsItem["last_time"], _ = jsonparser.GetString(value, "top", "hits","hits", "[0]", "_source","created")
stats = append(stats, statsItem)
}, "aggregations", grpKey, "buckets")
return stats
} }

View File

@ -190,7 +190,6 @@ func (alertAPI *AlertAPI) getRuleDetail(w http.ResponseWriter, req *http.Request
return return
} }
queryDSL := util.MapStr{ queryDSL := util.MapStr{
"_source": "state",
"size": 1, "size": 1,
"sort": []util.MapStr{ "sort": []util.MapStr{
{ {
@ -200,9 +199,22 @@ func (alertAPI *AlertAPI) getRuleDetail(w http.ResponseWriter, req *http.Request
}, },
}, },
"query": util.MapStr{ "query": util.MapStr{
"term": util.MapStr{ "bool": util.MapStr{
"rule_id": util.MapStr{ "must": []util.MapStr{
"value": obj.ID, {
"term": util.MapStr{
"rule_id": util.MapStr{
"value": obj.ID,
},
},
},
{
"term": util.MapStr{
"status": util.MapStr{
"value": alerting.MessageStateAlerting,
},
},
},
}, },
}, },
}, },
@ -211,18 +223,74 @@ func (alertAPI *AlertAPI) getRuleDetail(w http.ResponseWriter, req *http.Request
WildcardIndex: true, WildcardIndex: true,
RawQuery: util.MustToJSONBytes(queryDSL), RawQuery: util.MustToJSONBytes(queryDSL),
} }
err, result := orm.Search(alerting.Alert{}, q) err, result := orm.Search(alerting.AlertMessage{}, q)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
alertAPI.WriteJSON(w, util.MapStr{ alertAPI.WriteError(w, err.Error(), http.StatusInternalServerError)
"error": err.Error(),
}, http.StatusInternalServerError)
return return
} }
var state interface{} = "N/A" var state interface{} = "N/A"
var alertingMessageItem interface{}
if len(result.Result) > 0 { if len(result.Result) > 0 {
alertingMessageItem = result.Result[0]
if resultM, ok := result.Result[0].(map[string]interface{}); ok { if resultM, ok := result.Result[0].(map[string]interface{}); ok {
state = resultM["state"] state = resultM["status"]
}
}
var channelIDs []interface{}
if obj.NotificationConfig != nil {
for _, ch := range obj.NotificationConfig.Normal {
channelIDs = append(channelIDs, ch.ID)
}
for _, ch := range obj.NotificationConfig.Escalation {
channelIDs = append(channelIDs, ch.ID)
}
}
if obj.RecoveryNotificationConfig != nil {
for _, ch := range obj.RecoveryNotificationConfig.Normal {
channelIDs = append(channelIDs, ch.ID)
}
}
q = &orm.Query{
Size: len(channelIDs),
}
q.Conds = append(q.Conds, orm.In("id", channelIDs))
err, result = orm.Search(alerting.Channel{}, q)
if err != nil {
log.Error(err)
alertAPI.WriteError(w, err.Error(), http.StatusInternalServerError)
return
}
chm := map[string]alerting.Channel{}
for _, row := range result.Result {
buf := util.MustToJSONBytes(row)
ch := alerting.Channel{}
util.MustFromJSONBytes(buf, &ch)
chm[ch.ID] = ch
}
if obj.NotificationConfig != nil {
for i, ch := range obj.NotificationConfig.Normal {
if v, ok := chm[ch.ID]; ok {
obj.NotificationConfig.Normal[i].Enabled = v.Enabled && ch.Enabled
obj.NotificationConfig.Normal[i].Type = v.SubType
obj.NotificationConfig.Normal[i].Name = v.Name
}
}
for i, ch := range obj.NotificationConfig.Escalation {
if v, ok := chm[ch.ID]; ok {
obj.NotificationConfig.Escalation[i].Enabled = v.Enabled && ch.Enabled
obj.NotificationConfig.Escalation[i].Type = v.SubType
obj.NotificationConfig.Escalation[i].Name = v.Name
}
}
}
if obj.RecoveryNotificationConfig != nil {
for i, ch := range obj.RecoveryNotificationConfig.Normal {
if v, ok := chm[ch.ID]; ok {
obj.RecoveryNotificationConfig.Normal[i].Enabled = v.Enabled && ch.Enabled
obj.RecoveryNotificationConfig.Normal[i].Type = v.SubType
obj.RecoveryNotificationConfig.Normal[i].Name = v.Name
}
} }
} }
@ -231,12 +299,22 @@ func (alertAPI *AlertAPI) getRuleDetail(w http.ResponseWriter, req *http.Request
"resource_name": obj.Resource.Name, "resource_name": obj.Resource.Name,
"resource_id": obj.Resource.ID, "resource_id": obj.Resource.ID,
"resource_objects": obj.Resource.Objects, "resource_objects": obj.Resource.Objects,
"resource_time_field": obj.Resource.TimeField,
"resource_raw_filter": obj.Resource.RawFilter,
"metrics": obj.Metrics,
"bucket_size": obj.Metrics.BucketSize, //统计周期 "bucket_size": obj.Metrics.BucketSize, //统计周期
"updated": obj.Updated, "updated": obj.Updated,
"conditions": obj.Conditions, "conditions": obj.Conditions,
"message_count": alertNumbers[obj.ID], //所有关联告警消息数(包括已恢复的) "message_count": alertNumbers[obj.ID], //所有关联告警消息数(包括已恢复的)
"state": state, "state": state,
"enabled": obj.Enabled, "enabled": obj.Enabled,
"created": obj.Created,
"creator": obj.Creator,
"tags": obj.Tags,
"alerting_message": alertingMessageItem,
"expression": obj.Metrics.Expression,
"notification_config": obj.NotificationConfig,
"recovery_notification_config": obj.RecoveryNotificationConfig,
} }
alertAPI.WriteJSON(w, detailObj, 200) alertAPI.WriteJSON(w, detailObj, 200)
@ -418,7 +496,9 @@ func (alertAPI *AlertAPI) deleteRule(w http.ResponseWriter, req *http.Request, p
delDsl := util.MapStr{ delDsl := util.MapStr{
"query": util.MapStr{ "query": util.MapStr{
"term": util.MapStr{ "term": util.MapStr{
"rule_id": id, "rule_id": util.MapStr{
"value": id,
},
}, },
}, },
} }
@ -437,6 +517,69 @@ func (alertAPI *AlertAPI) deleteRule(w http.ResponseWriter, req *http.Request, p
}, 200) }, 200)
} }
func (alertAPI *AlertAPI) batchDeleteRule(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
var ruleIDs = []string{}
err := alertAPI.DecodeJSON(req, &ruleIDs)
if err != nil {
log.Error(err)
alertAPI.WriteError(w, err.Error(), http.StatusInternalServerError)
return
}
if len(ruleIDs) == 0 {
alertAPI.WriteJSON(w, util.MapStr{}, http.StatusOK)
return
}
rules, err := getRulesByID(ruleIDs)
if err != nil {
log.Error(err)
alertAPI.WriteError(w, err.Error(), http.StatusInternalServerError)
return
}
var newIDs []string
for _, rule := range rules {
saveAlertActivity("alerting_rule_change", "delete", util.MapStr{
"cluster_id": rule.Resource.ID,
"rule_id": rule.ID,
"cluster_name": rule.Resource.Name,
},nil, &rule)
task.DeleteTask(rule.ID)
clearKV(rule.ID)
newIDs = append(newIDs, rule.ID)
}
if len(newIDs) > 0 {
q := util.MapStr{
"query": util.MapStr{
"terms": util.MapStr{
"id": newIDs,
},
},
}
err = orm.DeleteBy(alerting.Rule{}, util.MustToJSONBytes(q))
if err != nil {
log.Error(err)
alertAPI.WriteError(w, err.Error(), http.StatusInternalServerError)
return
}
delDsl := util.MapStr{
"query": util.MapStr{
"terms": util.MapStr{
"rule_id": newIDs,
},
},
}
err = orm.DeleteBy(alerting.AlertMessage{}, util.MustToJSONBytes(delDsl))
if err != nil {
log.Error(err)
}
err = orm.DeleteBy(alerting.Alert{}, util.MustToJSONBytes(delDsl))
if err != nil {
log.Error(err)
}
}
alertAPI.WriteAckOKJSON(w)
}
func (alertAPI *AlertAPI) searchRule(w http.ResponseWriter, req *http.Request, ps httprouter.Params) { func (alertAPI *AlertAPI) searchRule(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
var ( var (
keyword = alertAPI.GetParameterOrDefault(req, "keyword", "") keyword = alertAPI.GetParameterOrDefault(req, "keyword", "")
@ -592,9 +735,7 @@ func (alertAPI *AlertAPI) fetchAlertInfos(w http.ResponseWriter, req *http.Reque
searchRes, err := esClient.SearchWithRawQueryDSL(orm.GetWildcardIndexName(alerting.Alert{}), util.MustToJSONBytes(queryDsl) ) searchRes, err := esClient.SearchWithRawQueryDSL(orm.GetWildcardIndexName(alerting.Alert{}), util.MustToJSONBytes(queryDsl) )
if err != nil { if err != nil {
log.Error(err) log.Error(err)
alertAPI.WriteJSON(w, util.MapStr{ alertAPI.WriteError(w, err.Error(), http.StatusInternalServerError)
"error": err.Error(),
}, http.StatusInternalServerError)
return return
} }
if len(searchRes.Hits.Hits) == 0 { if len(searchRes.Hits.Hits) == 0 {
@ -609,6 +750,50 @@ func (alertAPI *AlertAPI) fetchAlertInfos(w http.ResponseWriter, req *http.Reque
"status": hit.Source["state"], "status": hit.Source["state"],
} }
} }
}
queryDsl = util.MapStr{
"_source": []string{"created", "rule_id"},
"sort": []util.MapStr{
{
"created": util.MapStr{
"order": "desc",
},
},
},
"collapse": util.MapStr{
"field": "rule_id",
},
"query": util.MapStr{
"bool": util.MapStr{
"must": []util.MapStr{
{
"terms": util.MapStr{
"rule_id": ruleIDs,
},
},
{
"term": util.MapStr{
"state": util.MapStr{
"value": alerting.AlertStateAlerting,
},
},
},
},
},
},
}
searchRes, err = esClient.SearchWithRawQueryDSL(orm.GetWildcardIndexName(alerting.Alert{}), util.MustToJSONBytes(queryDsl) )
if err != nil {
log.Error(err)
alertAPI.WriteError(w, err.Error(), http.StatusInternalServerError)
return
}
for _, hit := range searchRes.Hits.Hits {
if ruleID, ok := hit.Source["rule_id"].(string); ok {
if _, ok = latestAlertInfos[ruleID]; ok {
latestAlertInfos[ruleID]["last_notification_time"] = hit.Source["created"]
}
}
} }
alertAPI.WriteJSON(w, latestAlertInfos, http.StatusOK) alertAPI.WriteJSON(w, latestAlertInfos, http.StatusOK)
@ -636,20 +821,9 @@ func (alertAPI *AlertAPI) enableRule(w http.ResponseWriter, req *http.Request, p
return return
} }
if reqObj.Enabled { if reqObj.Enabled {
eng := alerting2.GetEngine(obj.Resource.Type) enableRule(&obj)
ruleTask := task.ScheduleTask{ } else {
ID: obj.ID, disableRule(&obj)
Interval: obj.Schedule.Interval,
Description: obj.Metrics.Expression,
Task: eng.GenerateTask(obj),
}
task.DeleteTask(ruleTask.ID)
clearKV(ruleTask.ID)
task.RegisterScheduleTask(ruleTask)
task.StartTask(ruleTask.ID)
}else{
task.DeleteTask(id)
clearKV(id)
} }
obj.Enabled = reqObj.Enabled obj.Enabled = reqObj.Enabled
err = orm.Save(nil, obj) err = orm.Save(nil, obj)
@ -660,10 +834,28 @@ func (alertAPI *AlertAPI) enableRule(w http.ResponseWriter, req *http.Request, p
} }
alertAPI.WriteJSON(w, util.MapStr{ alertAPI.WriteJSON(w, util.MapStr{
"result": "updated", "result": "updated",
"_id": id, "_id": id,
}, http.StatusOK) }, http.StatusOK)
} }
func enableRule(obj *alerting.Rule) {
eng := alerting2.GetEngine(obj.Resource.Type)
ruleTask := task.ScheduleTask{
ID: obj.ID,
Interval: obj.Schedule.Interval,
Description: obj.Metrics.Expression,
Task: eng.GenerateTask(*obj),
}
task.DeleteTask(ruleTask.ID)
clearKV(ruleTask.ID)
task.RegisterScheduleTask(ruleTask)
task.StartTask(ruleTask.ID)
}
func disableRule(obj *alerting.Rule) {
task.DeleteTask(obj.ID)
clearKV(obj.ID)
}
func (alertAPI *AlertAPI) sendTestMessage(w http.ResponseWriter, req *http.Request, ps httprouter.Params) { func (alertAPI *AlertAPI) sendTestMessage(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
typ := alertAPI.GetParameterOrDefault(req, "type", "notification") typ := alertAPI.GetParameterOrDefault(req, "type", "notification")
rule := alerting.Rule{} rule := alerting.Rule{}
@ -917,4 +1109,183 @@ func getRuleMetricData( rule *alerting.Rule, filterParam *alerting.FilterParam)
}) })
} }
return &metricItem,queryResult, nil return &metricItem,queryResult, nil
}
func (alertAPI *AlertAPI) batchEnableRule(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
var ruleIDs = []string{}
err := alertAPI.DecodeJSON(req, &ruleIDs)
if err != nil {
log.Error(err)
alertAPI.WriteError(w, err.Error(), http.StatusInternalServerError)
return
}
if len(ruleIDs) == 0 {
alertAPI.WriteJSON(w, util.MapStr{}, http.StatusOK)
return
}
rules, err := getRulesByID(ruleIDs)
if err != nil {
log.Error(err)
alertAPI.WriteError(w, err.Error(), http.StatusInternalServerError)
return
}
var newIDs []string
for _, rule := range rules {
if !rule.Enabled {
enableRule(&rule)
newIDs = append(newIDs, rule.ID)
}
}
if len(newIDs) > 0 {
q := util.MapStr{
"query": util.MapStr{
"terms": util.MapStr{
"id": newIDs,
},
},
"script": util.MapStr{
"source": fmt.Sprintf("ctx._source['enabled'] = %v", true),
},
}
err = orm.UpdateBy(alerting.Rule{}, util.MustToJSONBytes(q))
if err != nil {
log.Error(err)
alertAPI.WriteError(w, err.Error(), http.StatusInternalServerError)
return
}
}
alertAPI.WriteAckOKJSON(w)
}
func (alertAPI *AlertAPI) batchDisableRule(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
var ruleIDs = []string{}
err := alertAPI.DecodeJSON(req, &ruleIDs)
if err != nil {
log.Error(err)
alertAPI.WriteError(w, err.Error(), http.StatusInternalServerError)
return
}
if len(ruleIDs) == 0 {
alertAPI.WriteJSON(w, util.MapStr{}, http.StatusOK)
return
}
rules, err := getRulesByID(ruleIDs)
if err != nil {
log.Error(err)
alertAPI.WriteError(w, err.Error(), http.StatusInternalServerError)
return
}
var newIDs []string
for _, rule := range rules {
if rule.Enabled {
disableRule(&rule)
newIDs = append(newIDs, rule.ID)
}
}
if len(newIDs) > 0 {
q := util.MapStr{
"query": util.MapStr{
"terms": util.MapStr{
"id": newIDs,
},
},
"script": util.MapStr{
"source": fmt.Sprintf("ctx._source['enabled'] = %v", false),
},
}
log.Info(util.MustToJSON(q))
err = orm.UpdateBy(alerting.Rule{}, util.MustToJSONBytes(q))
if err != nil {
log.Error(err)
alertAPI.WriteError(w, err.Error(), http.StatusInternalServerError)
return
}
}
alertAPI.WriteAckOKJSON(w)
}
func (alertAPI *AlertAPI) searchFieldValues(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
var keyword = alertAPI.GetParameterOrDefault(req, "keyword", "")
var field = alertAPI.GetParameterOrDefault(req, "field", "category")
items , err := searchListItems(field, keyword, 20)
if err != nil {
log.Error(err)
alertAPI.WriteError(w, err.Error(), http.StatusInternalServerError)
return
}
alertAPI.WriteJSON(w, items, http.StatusOK)
}
func searchListItems(field, keyword string, size int) ([]string, error){
query := util.MapStr{
"size": 0,
"aggs": util.MapStr{
"items": util.MapStr{
"terms": util.MapStr{
"field": field,
"size": size,
},
},
},
}
if v := strings.TrimSpace(keyword); v != ""{
query["query"]= util.MapStr{
"query_string": util.MapStr{
"default_field": field,
"query": fmt.Sprintf("*%s*", v),
},
}
}
q := orm.Query{
RawQuery: util.MustToJSONBytes(query),
}
err, result := orm.Search(alerting.Rule{}, &q)
if err != nil {
return nil, err
}
searchRes := elastic.SearchResponse{}
err = util.FromJSONBytes(result.Raw, &searchRes)
if err != nil {
return nil, err
}
items := []string{}
for _, bk := range searchRes.Aggregations["items"].Buckets {
if v, ok := bk["key"].(string); ok {
if strings.Contains(v, keyword){
items = append(items, v)
}
}
}
return items, nil
}
func getRulesByID(ruleIDs []string) ([]alerting.Rule, error){
if len(ruleIDs) == 0 {
return nil, nil
}
query := util.MapStr{
"size": len(ruleIDs),
"query": util.MapStr{
"terms": util.MapStr{
"id": ruleIDs,
},
},
}
q := &orm.Query{
RawQuery: util.MustToJSONBytes(query),
}
err, result := orm.Search(alerting.Rule{}, q)
if err != nil {
return nil, err
}
var rules []alerting.Rule
for _, row := range result.Result {
buf := util.MustToJSONBytes(row)
rule := alerting.Rule{}
util.MustFromJSONBytes(buf, &rule)
rules = append(rules, rule)
}
return rules, nil
} }

View File

@ -25,7 +25,7 @@ func (h *DataAPI) exportData(w http.ResponseWriter, req *http.Request, ps httpro
} }
var resBody []ExportData var resBody []ExportData
for _, meta := range reqBody.Metadatas { for _, meta := range reqBody.Metadatas {
result, err := getExportData(meta.Type) result, err := getExportData(meta)
if err != nil { if err != nil {
log.Error(err) log.Error(err)
h.WriteError(w, err.Error(), http.StatusInternalServerError) h.WriteError(w, err.Error(), http.StatusInternalServerError)
@ -86,9 +86,9 @@ func indexExportData(eds []ExportData) error {
return nil return nil
} }
func getExportData(typ string) (*orm.Result, error) { func getExportData(meta ExportMetadata) (*orm.Result, error) {
var obj interface{} var obj interface{}
switch typ { switch meta.Type {
case DataTypeAlertChannel: case DataTypeAlertChannel:
obj = alerting.Channel{} obj = alerting.Channel{}
case DataTypeAlertRule: case DataTypeAlertRule:
@ -96,11 +96,19 @@ func getExportData(typ string) (*orm.Result, error) {
case DataTypeAlertEmailServer: case DataTypeAlertEmailServer:
obj = model.EmailServer{} obj = model.EmailServer{}
default: default:
return nil, fmt.Errorf("unkonw data type: %s", typ) return nil, fmt.Errorf("unkonw data type: %s", meta.Type)
} }
err, result := orm.Search(obj, &orm.Query{ q := &orm.Query{
Size: 1000, Size: 1000,
}) }
if meta.Filter != nil {
query := util.MapStr{
"size": 1000,
"query": meta.Filter,
}
q.RawQuery = util.MustToJSONBytes(query)
}
err, result := orm.Search(obj, q)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -10,6 +10,7 @@ type ExportDataRequest struct {
type ExportMetadata struct { type ExportMetadata struct {
Type string `json:"type"` Type string `json:"type"`
Filter interface{} `json:"filter,omitempty"`
} }
type ExportData struct { type ExportData struct {

View File

@ -28,4 +28,6 @@ func InitAPI() {
api.HandleAPIMethod(api.DELETE, "/insight/dashboard/:dashboard_id", insight.deleteDashboard) api.HandleAPIMethod(api.DELETE, "/insight/dashboard/:dashboard_id", insight.deleteDashboard)
api.HandleAPIMethod(api.GET, "/insight/dashboard/_search", insight.searchDashboard) api.HandleAPIMethod(api.GET, "/insight/dashboard/_search", insight.searchDashboard)
api.HandleAPIMethod(api.POST, "/elasticsearch/:id/map_label/_render", insight.renderMapLabelTemplate) api.HandleAPIMethod(api.POST, "/elasticsearch/:id/map_label/_render", insight.renderMapLabelTemplate)
api.HandleAPIMethod(api.GET, "/insight/widget/:widget_id", insight.getWidget)
api.HandleAPIMethod(api.POST, "/insight/widget", insight.RequireLogin(insight.createWidget))
} }

View File

@ -0,0 +1,51 @@
/* Copyright © INFINI Ltd. All rights reserved.
* Web: https://infinilabs.com
* Email: hello#infini.ltd */
package insight
import (
log "github.com/cihub/seelog"
"infini.sh/console/model/insight"
httprouter "infini.sh/framework/core/api/router"
"infini.sh/framework/core/orm"
"infini.sh/framework/core/util"
"net/http"
)
func (h *InsightAPI) createWidget(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
var obj = &insight.Widget{}
err := h.DecodeJSON(req, obj)
if err != nil {
h.WriteError(w, err.Error(), http.StatusInternalServerError)
log.Error(err)
return
}
err = orm.Create(nil, obj)
if err != nil {
h.WriteError(w, err.Error(), http.StatusInternalServerError)
log.Error(err)
return
}
h.WriteCreatedOKJSON(w, obj.ID)
}
func (h *InsightAPI) getWidget(w http.ResponseWriter, req *http.Request, ps httprouter.Params) {
id := ps.MustGetParameter("widget_id")
obj := insight.Widget{}
obj.ID = id
exists, err := orm.Get(&obj)
if !exists || err != nil {
h.WriteJSON(w, util.MapStr{
"_id": id,
"found": false,
}, http.StatusNotFound)
return
}
h.WriteGetOKJSON(w, id, obj)
}

View File

@ -699,7 +699,10 @@ func (engine *Engine) Do(rule *alerting.Rule) error {
return err return err
} }
actionResults, _ := performChannels(recoverCfg.Normal, paramsCtx, false) actionResults, _ := performChannels(recoverCfg.Normal, paramsCtx, false)
alertItem.ActionExecutionResults = actionResults alertItem.RecoverActionResults = actionResults
//clear history notification time
_ = kv.DeleteKey(alerting2.KVLastNotificationTime, []byte(rule.ID))
_ = kv.DeleteKey(alerting2.KVLastEscalationTime, []byte(rule.ID))
} }
} }
return nil return nil
@ -745,6 +748,8 @@ func (engine *Engine) Do(rule *alerting.Rule) error {
Priority: priority, Priority: priority,
Title: alertItem.Title, Title: alertItem.Title,
Message: alertItem.Message, Message: alertItem.Message,
Tags: rule.Tags,
Category: rule.Category,
} }
err = saveAlertMessage(msg) err = saveAlertMessage(msg)
if err != nil { if err != nil {
@ -849,15 +854,13 @@ func (engine *Engine) Do(rule *alerting.Rule) error {
} }
} }
if time.Now().Sub(rule.LastEscalationTime.Local()) > periodDuration { if time.Now().Sub(rule.LastEscalationTime.Local()) > periodDuration {
actionResults, errCount := performChannels(notifyCfg.Escalation, paramsCtx, false) actionResults, _ := performChannels(notifyCfg.Escalation, paramsCtx, false)
alertItem.ActionExecutionResults = actionResults alertItem.EscalationActionResults = actionResults
//todo init last escalation time when create task (by last alert item is escalated) //todo init last escalation time when create task (by last alert item is escalated)
if errCount == 0 { rule.LastEscalationTime = time.Now()
rule.LastEscalationTime = time.Now() alertItem.IsEscalated = true
alertItem.IsEscalated = true strTime := rule.LastEscalationTime.UTC().Format(time.RFC3339)
strTime := rule.LastEscalationTime.UTC().Format(time.RFC3339) kv.AddValue(alerting2.KVLastEscalationTime, []byte(rule.ID), []byte(strTime))
kv.AddValue(alerting2.KVLastEscalationTime, []byte(rule.ID), []byte(strTime))
}
} }
} }
@ -1061,8 +1064,9 @@ func performChannels(channels []alerting.Channel, ctx map[string]interface{}, ra
Error: errStr, Error: errStr,
Message: string(messageBytes), Message: string(messageBytes),
ExecutionTime: int(time.Now().UnixNano()/1e6), ExecutionTime: int(time.Now().UnixNano()/1e6),
ChannelType: channel.Type, ChannelType: channel.SubType,
ChannelName: channel.Name, ChannelName: channel.Name,
ChannelID: channel.ID,
}) })
} }
return actionResults, errCount return actionResults, errCount