// Copyright 2017 Xiaomi, Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package judge import ( "fmt" "math" "github.com/didi/nightingale/v5/vos" ) type Function interface { Compute(vs []*vos.HPoint) (leftValue vos.JsonFloat, isTriggered bool) } type MaxFunction struct { Function Limit int Operator string RightValue float64 } func (f MaxFunction) Compute(vs []*vos.HPoint) (leftValue vos.JsonFloat, isTriggered bool) { count := len(vs) if count < 1 { return } max := vs[0].Value for i := 1; i < len(vs); i++ { if max < vs[i].Value { max = vs[i].Value } } leftValue = max isTriggered = checkIsTriggered(leftValue, f.Operator, f.RightValue) return } type MinFunction struct { Function Limit int Operator string RightValue float64 } func (f MinFunction) Compute(vs []*vos.HPoint) (leftValue vos.JsonFloat, isTriggered bool) { count := len(vs) if count < 1 { return } min := vs[0].Value for i := 1; i < len(vs); i++ { if min > vs[i].Value { min = vs[i].Value } } leftValue = min isTriggered = checkIsTriggered(leftValue, f.Operator, f.RightValue) return } type AllFunction struct { Function Limit int Operator string RightValue float64 } func (f AllFunction) Compute(vs []*vos.HPoint) (leftValue vos.JsonFloat, isTriggered bool) { count := len(vs) if count < 1 { return } for i := 0; i < len(vs); i++ { isTriggered = checkIsTriggered(vs[i].Value, f.Operator, f.RightValue) if !isTriggered { break } } leftValue = vs[0].Value return } type SumFunction struct { Function Limit int Operator string RightValue float64 } func (f SumFunction) Compute(vs []*vos.HPoint) (leftValue vos.JsonFloat, isTriggered bool) { count := len(vs) if count < 1 { return } sum := vos.JsonFloat(0.0) for i := 0; i < count; i++ { sum += vs[i].Value } leftValue = sum isTriggered = checkIsTriggered(leftValue, f.Operator, f.RightValue) return } type AvgFunction struct { Function Limit int Operator string RightValue float64 } func (f AvgFunction) Compute(vs []*vos.HPoint) (leftValue vos.JsonFloat, isTriggered bool) { vsLen := len(vs) if vsLen < 1 { return } sum := vos.JsonFloat(0.0) for i := 0; i < vsLen; i++ { sum += vs[i].Value } leftValue = sum / vos.JsonFloat(vsLen) isTriggered = checkIsTriggered(leftValue, f.Operator, f.RightValue) return } type StddevFunction struct { Function Num int Limit int } func (f StddevFunction) Compute(vs []*vos.HPoint) (leftValue vos.JsonFloat, isTriggered bool) { var sum float64 vsLen := len(vs) if vsLen < 1 { return } for i := 0; i < vsLen; i++ { sum += float64(vs[i].Value) } mean := sum / float64(vsLen) var num float64 for i := 0; i < vsLen; i++ { num += math.Pow(float64(vs[i].Value)-mean, 2) } std := math.Sqrt(num / float64(vsLen)) upperBound := mean + std*float64(f.Num) lowerBound := mean - std*float64(f.Num) leftValue = vs[0].Value isTriggered = checkIsTriggered(leftValue, "<", lowerBound) || checkIsTriggered(leftValue, ">", upperBound) return } type DiffFunction struct { Function Limit int Operator string RightValue float64 } // 只要有一个点的diff触发阈值,就报警 func (f DiffFunction) Compute(vs []*vos.HPoint) (leftValue vos.JsonFloat, isTriggered bool) { vsLen := len(vs) if vsLen < 1 { return } first := vs[0].Value isTriggered = false for i := 1; i < vsLen; i++ { // diff是当前值减去历史值 leftValue = first - vs[i].Value isTriggered = checkIsTriggered(leftValue, f.Operator, f.RightValue) if isTriggered { break } } return } // pdiff(#3) type PDiffFunction struct { Function Limit int Operator string RightValue float64 } func (f PDiffFunction) Compute(vs []*vos.HPoint) (leftValue vos.JsonFloat, isTriggered bool) { vsLen := len(vs) if vsLen < 1 { return } first := vs[0].Value isTriggered = false for i := 1; i < len(vs); i++ { if vs[i].Value == 0 { continue } leftValue = (first - vs[i].Value) / vs[i].Value * 100.0 isTriggered = checkIsTriggered(leftValue, f.Operator, f.RightValue) if isTriggered { break } } return } type HappenFunction struct { Function Num int Limit int Operator string RightValue float64 } func (f HappenFunction) Compute(vs []*vos.HPoint) (leftValue vos.JsonFloat, isTriggered bool) { for n, i := 0, 0; i < len(vs); i++ { if checkIsTriggered(vs[i].Value, f.Operator, f.RightValue) { n++ if n == f.Num { isTriggered = true leftValue = vs[i].Value return } } } return } type CAvgAbsFunction struct { Function Limit int Operator string RightValue float64 CompareValue float64 } func (f CAvgAbsFunction) Compute(vs []*vos.HPoint) (leftValue vos.JsonFloat, isTriggered bool) { vsLen := len(vs) if vsLen < 1 { return } sum := vos.JsonFloat(0.0) for i := 0; i < vsLen; i++ { sum += vs[i].Value } value := sum / vos.JsonFloat(vsLen) leftValue = vos.JsonFloat(math.Abs(float64(value) - float64(f.CompareValue))) isTriggered = checkIsTriggered(leftValue, f.Operator, f.RightValue) return } type CAvgFunction struct { Function Limit int Operator string RightValue float64 CompareValue float64 } func (f CAvgFunction) Compute(vs []*vos.HPoint) (leftValue vos.JsonFloat, isTriggered bool) { vsLen := len(vs) if vsLen < 1 { return } sum := vos.JsonFloat(0.0) for i := 0; i < vsLen; i++ { sum += vs[i].Value } leftValue = sum/vos.JsonFloat(vsLen) - vos.JsonFloat(f.CompareValue) isTriggered = checkIsTriggered(leftValue, f.Operator, f.RightValue) return } type CAvgRateAbsFunction struct { Function Limit int Operator string RightValue float64 CompareValue float64 } func (f CAvgRateAbsFunction) Compute(vs []*vos.HPoint) (leftValue vos.JsonFloat, isTriggered bool) { vsLen := len(vs) if vsLen < 1 { return } sum := vos.JsonFloat(0.0) for i := 0; i < vsLen; i++ { sum += vs[i].Value } value := sum / vos.JsonFloat(vsLen) leftValue = vos.JsonFloat(math.Abs((float64(value)-float64(f.CompareValue))/f.CompareValue)) * 100.00 isTriggered = checkIsTriggered(leftValue, f.Operator, f.RightValue) return } type CAvgRateFunction struct { Function Limit int Operator string RightValue float64 CompareValue float64 } func (f CAvgRateFunction) Compute(vs []*vos.HPoint) (leftValue vos.JsonFloat, isTriggered bool) { vsLen := len(vs) if vsLen < 1 { return } sum := vos.JsonFloat(0.0) for i := 0; i < vsLen; i++ { sum += vs[i].Value } value := sum / vos.JsonFloat(vsLen) leftValue = (value - vos.JsonFloat(f.CompareValue)) / vos.JsonFloat(math.Abs(f.CompareValue)) * 100.00 isTriggered = checkIsTriggered(leftValue, f.Operator, f.RightValue) return } func ParseFuncFromString(str string, span []interface{}, operator string, rightValue float64) (fn Function, err error) { if str == "" { return nil, fmt.Errorf("func can not be null") } limit := span[0].(int) switch str { case "max": fn = &MaxFunction{Limit: limit, Operator: operator, RightValue: rightValue} case "min": fn = &MinFunction{Limit: limit, Operator: operator, RightValue: rightValue} case "all": fn = &AllFunction{Limit: limit, Operator: operator, RightValue: rightValue} case "sum": fn = &SumFunction{Limit: limit, Operator: operator, RightValue: rightValue} case "avg": fn = &AvgFunction{Limit: limit, Operator: operator, RightValue: rightValue} case "stddev": fn = &StddevFunction{Limit: limit, Num: span[1].(int)} case "diff": fn = &DiffFunction{Limit: limit, Operator: operator, RightValue: rightValue} case "pdiff": fn = &PDiffFunction{Limit: limit, Operator: operator, RightValue: rightValue} case "happen": fn = &HappenFunction{Limit: limit, Num: span[1].(int), Operator: operator, RightValue: rightValue} case "c_avg": fn = &CAvgFunction{Limit: limit, CompareValue: span[1].(float64), Operator: operator, RightValue: rightValue} case "c_avg_abs": fn = &CAvgAbsFunction{Limit: limit, CompareValue: span[1].(float64), Operator: operator, RightValue: rightValue} case "c_avg_rate": fn = &CAvgRateFunction{Limit: limit, CompareValue: span[1].(float64), Operator: operator, RightValue: rightValue} case "c_avg_rate_abs": fn = &CAvgRateAbsFunction{Limit: limit, CompareValue: span[1].(float64), Operator: operator, RightValue: rightValue} default: err = fmt.Errorf("not_supported_method") } return } func checkIsTriggered(leftValue vos.JsonFloat, operator string, rightValue float64) (isTriggered bool) { switch operator { case "=", "==": isTriggered = math.Abs(float64(leftValue)-rightValue) < 0.0001 case "!=": isTriggered = math.Abs(float64(leftValue)-rightValue) > 0.0001 case "<": isTriggered = float64(leftValue) < rightValue case "<=": isTriggered = float64(leftValue) <= rightValue case ">": isTriggered = float64(leftValue) > rightValue case ">=": isTriggered = float64(leftValue) >= rightValue } return }