| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191 |
- package alerting
- import (
- "encoding/json"
- "fmt"
- "github.com/grafana/grafana/pkg/bus"
- "github.com/grafana/grafana/pkg/components/simplejson"
- m "github.com/grafana/grafana/pkg/models"
- "github.com/grafana/grafana/pkg/tsdb"
- )
- type QueryCondition struct {
- Index int
- Query AlertQuery
- Reducer QueryReducer
- Evaluator AlertEvaluator
- HandleRequest tsdb.HandleRequestFunc
- }
- func (c *QueryCondition) Eval(context *AlertResultContext) {
- seriesList, err := c.executeQuery(context)
- if err != nil {
- context.Error = err
- return
- }
- for _, series := range seriesList {
- reducedValue := c.Reducer.Reduce(series)
- pass := c.Evaluator.Eval(series, reducedValue)
- if context.IsTestRun {
- context.Logs = append(context.Logs, &AlertResultLogEntry{
- Message: fmt.Sprintf("Condition[%d]: Eval: %v, Metric: %s, Value: %1.3f", c.Index, pass, series.Name, reducedValue),
- })
- }
- if pass {
- context.Events = append(context.Events, &AlertEvent{
- Metric: series.Name,
- Value: reducedValue,
- })
- context.Firing = true
- break
- }
- }
- }
- func (c *QueryCondition) executeQuery(context *AlertResultContext) (tsdb.TimeSeriesSlice, error) {
- getDsInfo := &m.GetDataSourceByIdQuery{
- Id: c.Query.DatasourceId,
- OrgId: context.Rule.OrgId,
- }
- if err := bus.Dispatch(getDsInfo); err != nil {
- return nil, fmt.Errorf("Could not find datasource")
- }
- req := c.getRequestForAlertRule(getDsInfo.Result)
- result := make(tsdb.TimeSeriesSlice, 0)
- resp, err := c.HandleRequest(req)
- if err != nil {
- return nil, fmt.Errorf("tsdb.HandleRequest() error %v", err)
- }
- for _, v := range resp.Results {
- if v.Error != nil {
- return nil, fmt.Errorf("tsdb.HandleRequest() response error %v", v)
- }
- result = append(result, v.Series...)
- if context.IsTestRun {
- context.Logs = append(context.Logs, &AlertResultLogEntry{
- Message: fmt.Sprintf("Condition[%d]: Query Result", c.Index),
- Data: v.Series,
- })
- }
- }
- return result, nil
- }
- func (c *QueryCondition) getRequestForAlertRule(datasource *m.DataSource) *tsdb.Request {
- req := &tsdb.Request{
- TimeRange: tsdb.TimeRange{
- From: c.Query.From,
- To: c.Query.To,
- },
- Queries: []*tsdb.Query{
- {
- RefId: "A",
- Query: c.Query.Model.Get("target").MustString(),
- DataSource: &tsdb.DataSourceInfo{
- Id: datasource.Id,
- Name: datasource.Name,
- PluginId: datasource.Type,
- Url: datasource.Url,
- },
- },
- },
- }
- return req
- }
- func NewQueryCondition(model *simplejson.Json, index int) (*QueryCondition, error) {
- condition := QueryCondition{}
- condition.Index = index
- condition.HandleRequest = tsdb.HandleRequest
- queryJson := model.Get("query")
- condition.Query.Model = queryJson.Get("model")
- condition.Query.From = queryJson.Get("params").MustArray()[1].(string)
- condition.Query.To = queryJson.Get("params").MustArray()[2].(string)
- condition.Query.DatasourceId = queryJson.Get("datasourceId").MustInt64()
- reducerJson := model.Get("reducer")
- condition.Reducer = NewSimpleReducer(reducerJson.Get("type").MustString())
- evaluatorJson := model.Get("evaluator")
- evaluator, err := NewDefaultAlertEvaluator(evaluatorJson)
- if err != nil {
- return nil, err
- }
- condition.Evaluator = evaluator
- return &condition, nil
- }
- type SimpleReducer struct {
- Type string
- }
- func (s *SimpleReducer) Reduce(series *tsdb.TimeSeries) float64 {
- var value float64 = 0
- switch s.Type {
- case "avg":
- for _, point := range series.Points {
- value += point[0]
- }
- value = value / float64(len(series.Points))
- }
- return value
- }
- func NewSimpleReducer(typ string) *SimpleReducer {
- return &SimpleReducer{Type: typ}
- }
- type DefaultAlertEvaluator struct {
- Type string
- Threshold float64
- }
- func (e *DefaultAlertEvaluator) Eval(series *tsdb.TimeSeries, reducedValue float64) bool {
- switch e.Type {
- case ">":
- return reducedValue > e.Threshold
- case "<":
- return reducedValue < e.Threshold
- }
- return false
- }
- func NewDefaultAlertEvaluator(model *simplejson.Json) (*DefaultAlertEvaluator, error) {
- evaluator := &DefaultAlertEvaluator{}
- evaluator.Type = model.Get("type").MustString()
- if evaluator.Type == "" {
- return nil, AlertValidationError{Reason: "Evaluator missing type property"}
- }
- params := model.Get("params").MustArray()
- if len(params) == 0 {
- return nil, AlertValidationError{Reason: "Evaluator missing threshold parameter"}
- }
- threshold, ok := params[0].(json.Number)
- if !ok {
- return nil, AlertValidationError{Reason: "Evaluator has invalid threshold parameter"}
- }
- evaluator.Threshold, _ = threshold.Float64()
- return evaluator, nil
- }
|