update rule expression
This commit is contained in:
parent
6ac22f53a3
commit
2461773428
|
@ -42,4 +42,13 @@ const (
|
||||||
AlertStateAcknowledge = "acknowledge"
|
AlertStateAcknowledge = "acknowledge"
|
||||||
AlertStateNormal = "normal"
|
AlertStateNormal = "normal"
|
||||||
AlertStateError = "error"
|
AlertStateError = "error"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
{
|
||||||
|
RuleID
|
||||||
|
ResourceID
|
||||||
|
ResourceName
|
||||||
|
}
|
||||||
|
*/
|
|
@ -4,6 +4,8 @@
|
||||||
|
|
||||||
package alerting
|
package alerting
|
||||||
|
|
||||||
|
import "fmt"
|
||||||
|
|
||||||
type Condition struct {
|
type Condition struct {
|
||||||
Operator string `json:"operator"`
|
Operator string `json:"operator"`
|
||||||
Items []ConditionItem `json:"items"`
|
Items []ConditionItem `json:"items"`
|
||||||
|
@ -17,6 +19,32 @@ type ConditionItem struct {
|
||||||
Severity string `json:"severity"`
|
Severity string `json:"severity"`
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
}
|
}
|
||||||
|
func (cond *ConditionItem) GenerateConditionExpression()(conditionExpression string, err error){
|
||||||
|
valueLength := len(cond.Values)
|
||||||
|
if valueLength == 0 {
|
||||||
|
return conditionExpression, fmt.Errorf("condition values: %v should not be empty", cond.Values)
|
||||||
|
}
|
||||||
|
switch cond.Operator {
|
||||||
|
case "equals":
|
||||||
|
conditionExpression = fmt.Sprintf("result == %v", cond.Values[0])
|
||||||
|
case "gte":
|
||||||
|
conditionExpression = fmt.Sprintf("result >= %v", cond.Values[0])
|
||||||
|
case "lte":
|
||||||
|
conditionExpression = fmt.Sprintf("result <= %v", cond.Values[0])
|
||||||
|
case "gt":
|
||||||
|
conditionExpression = fmt.Sprintf("result > %v", cond.Values[0])
|
||||||
|
case "lt":
|
||||||
|
conditionExpression = fmt.Sprintf("result < %v", cond.Values[0])
|
||||||
|
case "range":
|
||||||
|
if len(cond.Values) != 2 {
|
||||||
|
return conditionExpression, fmt.Errorf("length of %s condition values should be 2", cond.Operator)
|
||||||
|
}
|
||||||
|
conditionExpression = fmt.Sprintf("result >= %v && result <= %v", cond.Values[0], cond.Values[1])
|
||||||
|
default:
|
||||||
|
return conditionExpression, fmt.Errorf("unsupport condition operator: %s", cond.Operator)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
type ConditionResult struct {
|
type ConditionResult struct {
|
||||||
ResultItems []ConditionResultItem `json:"result_items"`
|
ResultItems []ConditionResultItem `json:"result_items"`
|
||||||
|
|
|
@ -16,13 +16,12 @@ type Metric struct {
|
||||||
Formula string `json:"formula,omitempty"`
|
Formula string `json:"formula,omitempty"`
|
||||||
Expression string `json:"expression" elastic_mapping:"expression:{type:keyword,copy_to:search_text}"` //告警表达式,自动生成 eg: avg(cpu) > 80
|
Expression string `json:"expression" elastic_mapping:"expression:{type:keyword,copy_to:search_text}"` //告警表达式,自动生成 eg: avg(cpu) > 80
|
||||||
}
|
}
|
||||||
func (m *Metric) RefreshExpression() error{
|
func (m *Metric) GenerateExpression() (string, error){
|
||||||
if len(m.Items) == 1 {
|
if len(m.Items) == 1 {
|
||||||
m.Expression = fmt.Sprintf("%s(%s)", m.Items[0].Statistic, m.Items[0].Field)
|
return fmt.Sprintf("%s(%s)", m.Items[0].Statistic, m.Items[0].Field), nil
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
if m.Formula == "" {
|
if m.Formula == "" {
|
||||||
return fmt.Errorf("formula should not be empty since there are %d metrics", len(m.Items))
|
return "", fmt.Errorf("formula should not be empty since there are %d metrics", len(m.Items))
|
||||||
}
|
}
|
||||||
var (
|
var (
|
||||||
expressionBytes = []byte(m.Formula)
|
expressionBytes = []byte(m.Formula)
|
||||||
|
@ -32,13 +31,12 @@ func (m *Metric) RefreshExpression() error{
|
||||||
metricExpression = fmt.Sprintf("%s(%s)", item.Statistic, item.Field)
|
metricExpression = fmt.Sprintf("%s(%s)", item.Statistic, item.Field)
|
||||||
reg, err := regexp.Compile(item.Name+`([^\w]|$)`)
|
reg, err := regexp.Compile(item.Name+`([^\w]|$)`)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return "", err
|
||||||
}
|
}
|
||||||
expressionBytes = reg.ReplaceAll(expressionBytes, []byte(metricExpression+"$1"))
|
expressionBytes = reg.ReplaceAll(expressionBytes, []byte(metricExpression+"$1"))
|
||||||
}
|
}
|
||||||
|
|
||||||
m.Expression = string(expressionBytes)
|
return string(expressionBytes), nil
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type MetricItem struct {
|
type MetricItem struct {
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
package alerting
|
package alerting
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -23,6 +24,31 @@ type Rule struct {
|
||||||
LastTermStartTime time.Time `json:"-"` //标识最近一轮告警的开始时间
|
LastTermStartTime time.Time `json:"-"` //标识最近一轮告警的开始时间
|
||||||
LastEscalationTime time.Time `json:"-"` //标识最近一次告警升级发送通知的时间
|
LastEscalationTime time.Time `json:"-"` //标识最近一次告警升级发送通知的时间
|
||||||
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 }"`
|
||||||
|
Expression string `json:"-"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rule *Rule) GetOrInitExpression() (string, error){
|
||||||
|
if rule.Expression != ""{
|
||||||
|
return rule.Expression, nil
|
||||||
|
}
|
||||||
|
sb := strings.Builder{}
|
||||||
|
for i, cond := range rule.Conditions.Items {
|
||||||
|
condExp, err := cond.GenerateConditionExpression()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
sb.WriteString(condExp)
|
||||||
|
|
||||||
|
if i < len(rule.Conditions.Items)-1 {
|
||||||
|
sb.WriteString(" or ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
metricExp, err := rule.Metrics.GenerateExpression()
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
rule.Expression = strings.ReplaceAll(sb.String(), "result", metricExp)
|
||||||
|
return rule.Expression, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type RuleChannel struct {
|
type RuleChannel struct {
|
||||||
|
|
|
@ -25,7 +25,7 @@ func TestCreateRule( t *testing.T) {
|
||||||
Type: "elasticsearch",
|
Type: "elasticsearch",
|
||||||
Objects: []string{".infini_metrics*"},
|
Objects: []string{".infini_metrics*"},
|
||||||
TimeField: "timestamp",
|
TimeField: "timestamp",
|
||||||
Filter: Filter{
|
Filter: FilterQuery{
|
||||||
And: []FilterQuery{
|
And: []FilterQuery{
|
||||||
//{Field: "timestamp", Operator: "gte", Values: []string{"now-15m"}},
|
//{Field: "timestamp", Operator: "gte", Values: []string{"now-15m"}},
|
||||||
//{Field: "payload.elasticsearch.cluster_health.status", Operator: "equals", Values: []string{"red"}},
|
//{Field: "payload.elasticsearch.cluster_health.status", Operator: "equals", Values: []string{"red"}},
|
||||||
|
@ -106,13 +106,17 @@ func TestCreateRule( t *testing.T) {
|
||||||
EscalationThrottlePeriod: "30m",
|
EscalationThrottlePeriod: "30m",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
//err := rule.Metrics.RefreshExpression()
|
//err := rule.Metrics.GenerateExpression()
|
||||||
//if err != nil {
|
//if err != nil {
|
||||||
// t.Fatal(err)
|
// t.Fatal(err)
|
||||||
//}
|
//}
|
||||||
|
exp, err := rule.GetOrInitExpression()
|
||||||
|
if err != nil {
|
||||||
|
t.Fatal(err)
|
||||||
|
}
|
||||||
|
|
||||||
fmt.Println(util.MustToJSON(rule))
|
//fmt.Println(util.MustToJSON(rule))
|
||||||
//fmt.Println(rule.Metrics.Expression)
|
fmt.Println(exp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -37,7 +37,7 @@ func (alertAPI *AlertAPI) createRule(w http.ResponseWriter, req *http.Request, p
|
||||||
}, http.StatusInternalServerError)
|
}, http.StatusInternalServerError)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = rule.Metrics.RefreshExpression()
|
rule.Metrics.Expression, err = rule.Metrics.GenerateExpression()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
alertAPI.WriteJSON(w, util.MapStr{
|
alertAPI.WriteJSON(w, util.MapStr{
|
||||||
"error": err.Error(),
|
"error": err.Error(),
|
||||||
|
@ -135,7 +135,7 @@ func (alertAPI *AlertAPI) updateRule(w http.ResponseWriter, req *http.Request, p
|
||||||
obj.ID = id
|
obj.ID = id
|
||||||
obj.Created = create
|
obj.Created = create
|
||||||
obj.Updated = time.Now()
|
obj.Updated = time.Now()
|
||||||
err = obj.Metrics.RefreshExpression()
|
obj.Metrics.Expression, err = obj.Metrics.GenerateExpression()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
alertAPI.WriteError(w, err.Error(), http.StatusInternalServerError)
|
alertAPI.WriteError(w, err.Error(), http.StatusInternalServerError)
|
||||||
log.Error(err)
|
log.Error(err)
|
||||||
|
|
|
@ -424,29 +424,9 @@ func (engine *Engine) CheckCondition(rule *alerting.Rule)(*alerting.ConditionRes
|
||||||
})
|
})
|
||||||
LoopCondition:
|
LoopCondition:
|
||||||
for _, cond := range rule.Conditions.Items {
|
for _, cond := range rule.Conditions.Items {
|
||||||
conditionExpression := ""
|
conditionExpression, err := cond.GenerateConditionExpression()
|
||||||
valueLength := len(cond.Values)
|
if err != nil {
|
||||||
if valueLength == 0 {
|
return conditionResult, err
|
||||||
return conditionResult, fmt.Errorf("condition values: %v should not be empty", cond.Values)
|
|
||||||
}
|
|
||||||
switch cond.Operator {
|
|
||||||
case "equals":
|
|
||||||
conditionExpression = fmt.Sprintf("result == %v", cond.Values[0])
|
|
||||||
case "gte":
|
|
||||||
conditionExpression = fmt.Sprintf("result >= %v", cond.Values[0])
|
|
||||||
case "lte":
|
|
||||||
conditionExpression = fmt.Sprintf("result <= %v", cond.Values[0])
|
|
||||||
case "gt":
|
|
||||||
conditionExpression = fmt.Sprintf("result > %v", cond.Values[0])
|
|
||||||
case "lt":
|
|
||||||
conditionExpression = fmt.Sprintf("result < %v", cond.Values[0])
|
|
||||||
case "range":
|
|
||||||
if valueLength != 2 {
|
|
||||||
return conditionResult, fmt.Errorf("length of %s condition values should be 2", cond.Operator)
|
|
||||||
}
|
|
||||||
conditionExpression = fmt.Sprintf("result >= %v && result <= %v", cond.Values[0], cond.Values[1])
|
|
||||||
default:
|
|
||||||
return conditionResult, fmt.Errorf("unsupport condition operator: %s", cond.Operator)
|
|
||||||
}
|
}
|
||||||
expression, err := govaluate.NewEvaluableExpression(conditionExpression)
|
expression, err := govaluate.NewEvaluableExpression(conditionExpression)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -882,4 +862,4 @@ func readTimeFromKV(bucketKey string, key []byte)(time.Time, error){
|
||||||
return time.ParseInLocation(time.RFC3339, string(timeBytes), time.UTC)
|
return time.ParseInLocation(time.RFC3339, string(timeBytes), time.UTC)
|
||||||
}
|
}
|
||||||
return zeroTime, nil
|
return zeroTime, nil
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue