func (c *QueryCondition) Eval(context *alerting.EvalContext) { timeRange := tsdb.NewTimeRange(c.Query.From, c.Query.To) seriesList, err := c.executeQuery(context, timeRange) if err != nil { context.Error = err return } emptySerieCount := 0 for _, series := range seriesList { reducedValue := c.Reducer.Reduce(series) evalMatch := c.Evaluator.Eval(reducedValue) if reducedValue.Valid == false { emptySerieCount++ continue } if context.IsTestRun { context.Logs = append(context.Logs, &alerting.ResultLogEntry{ Message: fmt.Sprintf("Condition[%d]: Eval: %v, Metric: %s, Value: %1.3f", c.Index, evalMatch, series.Name, reducedValue.Float64), }) } if evalMatch { context.EvalMatches = append(context.EvalMatches, &alerting.EvalMatch{ Metric: series.Name, Value: reducedValue.Float64, }) } } context.NoDataFound = emptySerieCount == len(seriesList) context.Firing = len(context.EvalMatches) > 0 }
func (c *QueryCondition) Eval(context *alerting.EvalContext) (*alerting.ConditionResult, error) { timeRange := tsdb.NewTimeRange(c.Query.From, c.Query.To) seriesList, err := c.executeQuery(context, timeRange) if err != nil { return nil, err } emptySerieCount := 0 evalMatchCount := 0 var matches []*alerting.EvalMatch for _, series := range seriesList { reducedValue := c.Reducer.Reduce(series) evalMatch := c.Evaluator.Eval(reducedValue) if reducedValue.Valid == false { emptySerieCount++ } if context.IsTestRun { context.Logs = append(context.Logs, &alerting.ResultLogEntry{ Message: fmt.Sprintf("Condition[%d]: Eval: %v, Metric: %s, Value: %s", c.Index, evalMatch, series.Name, reducedValue), }) } if evalMatch { evalMatchCount++ matches = append(matches, &alerting.EvalMatch{ Metric: series.Name, Value: reducedValue, }) } } // handle no series special case if len(seriesList) == 0 { // eval condition for null value evalMatch := c.Evaluator.Eval(null.FloatFromPtr(nil)) if context.IsTestRun { context.Logs = append(context.Logs, &alerting.ResultLogEntry{ Message: fmt.Sprintf("Condition[%d]: Eval: %v, Query Returned No Series (reduced to null/no value)", evalMatch), }) } if evalMatch { evalMatchCount++ matches = append(matches, &alerting.EvalMatch{Metric: "NoData", Value: null.FloatFromPtr(nil)}) } } return &alerting.ConditionResult{ Firing: evalMatchCount > 0, NoDataFound: emptySerieCount == len(seriesList), Operator: c.Operator, EvalMatches: matches, }, nil }
// POST /api/tsdb/query func QueryMetrics(c *middleware.Context, reqDto dtos.MetricRequest) Response { timeRange := tsdb.NewTimeRange(reqDto.From, reqDto.To) request := &tsdb.Request{TimeRange: timeRange} for _, query := range reqDto.Queries { request.Queries = append(request.Queries, &tsdb.Query{ RefId: query.Get("refId").MustString("A"), MaxDataPoints: query.Get("maxDataPoints").MustInt64(100), IntervalMs: query.Get("intervalMs").MustInt64(1000), Model: query, DataSource: &models.DataSource{ Name: "Grafana TestDataDB", Type: "grafana-testdata-datasource", }, }) } resp, err := tsdb.HandleRequest(context.TODO(), request) if err != nil { return ApiError(500, "Metric request error", err) } return Json(200, &resp) }
func (c *QueryCondition) Eval(context *alerting.EvalContext) (*alerting.ConditionResult, error) { timeRange := tsdb.NewTimeRange(c.Query.From, c.Query.To) seriesList, err := c.executeQuery(context, timeRange) if err != nil { return nil, err } emptySerieCount := 0 evalMatchCount := 0 var matches []*alerting.EvalMatch for _, series := range seriesList { reducedValue := c.Reducer.Reduce(series) evalMatch := c.Evaluator.Eval(reducedValue) if reducedValue.Valid == false { emptySerieCount++ continue } if context.IsTestRun { context.Logs = append(context.Logs, &alerting.ResultLogEntry{ Message: fmt.Sprintf("Condition[%d]: Eval: %v, Metric: %s, Value: %1.3f", c.Index, evalMatch, series.Name, reducedValue.Float64), }) } if evalMatch { evalMatchCount++ matches = append(matches, &alerting.EvalMatch{ Metric: series.Name, Value: reducedValue.Float64, }) } } return &alerting.ConditionResult{ Firing: evalMatchCount > 0, NoDataFound: emptySerieCount == len(seriesList), Operator: c.Operator, EvalMatches: matches, }, nil }
func TestInfluxdbQueryPart(t *testing.T) { Convey("Influxdb query parts", t, func() { queryContext := &tsdb.QueryContext{TimeRange: tsdb.NewTimeRange("5m", "now")} query := &Query{} Convey("render field ", func() { part, err := NewQueryPart("field", []string{"value"}) So(err, ShouldBeNil) res := part.Render(query, queryContext, "value") So(res, ShouldEqual, `"value"`) }) Convey("render nested part", func() { part, err := NewQueryPart("derivative", []string{"10s"}) So(err, ShouldBeNil) res := part.Render(query, queryContext, "mean(value)") So(res, ShouldEqual, "derivative(mean(value), 10s)") }) Convey("render bottom", func() { part, err := NewQueryPart("bottom", []string{"3"}) So(err, ShouldBeNil) res := part.Render(query, queryContext, "value") So(res, ShouldEqual, "bottom(value, 3)") }) Convey("render time with $interval", func() { part, err := NewQueryPart("time", []string{"$interval"}) So(err, ShouldBeNil) res := part.Render(query, queryContext, "") So(res, ShouldEqual, "time($interval)") }) Convey("render time with auto", func() { part, err := NewQueryPart("time", []string{"auto"}) So(err, ShouldBeNil) res := part.Render(query, queryContext, "") So(res, ShouldEqual, "time($__interval)") }) Convey("render spread", func() { part, err := NewQueryPart("spread", []string{}) So(err, ShouldBeNil) res := part.Render(query, queryContext, "value") So(res, ShouldEqual, `spread(value)`) }) Convey("render suffix", func() { part, err := NewQueryPart("math", []string{"/ 100"}) So(err, ShouldBeNil) res := part.Render(query, queryContext, "mean(value)") So(res, ShouldEqual, "mean(value) / 100") }) Convey("render alias", func() { part, err := NewQueryPart("alias", []string{"test"}) So(err, ShouldBeNil) res := part.Render(query, queryContext, "mean(value)") So(res, ShouldEqual, `mean(value) AS "test"`) }) }) }
func TestInfluxdbQueryBuilder(t *testing.T) { Convey("Influxdb query builder", t, func() { builder := QueryBuilder{} qp1, _ := NewQueryPart("field", []string{"value"}) qp2, _ := NewQueryPart("mean", []string{}) groupBy1, _ := NewQueryPart("time", []string{"$interval"}) groupBy2, _ := NewQueryPart("tag", []string{"datacenter"}) groupBy3, _ := NewQueryPart("fill", []string{"null"}) tag1 := &Tag{Key: "hostname", Value: "server1", Operator: "="} tag2 := &Tag{Key: "hostname", Value: "server2", Operator: "=", Condition: "OR"} queryContext := &tsdb.QueryContext{ TimeRange: tsdb.NewTimeRange("5m", "now"), } Convey("can build simple query", func() { query := &Query{ Selects: []*Select{{*qp1, *qp2}}, Measurement: "cpu", Policy: "policy", GroupBy: []*QueryPart{groupBy1, groupBy3}, Interval: "10s", } rawQuery, err := builder.Build(query, queryContext) So(err, ShouldBeNil) So(rawQuery, ShouldEqual, `SELECT mean("value") FROM "policy"."cpu" WHERE time > now() - 5m GROUP BY time(10s) fill(null)`) }) Convey("can build query with group bys", func() { query := &Query{ Selects: []*Select{{*qp1, *qp2}}, Measurement: "cpu", GroupBy: []*QueryPart{groupBy1, groupBy2, groupBy3}, Tags: []*Tag{tag1, tag2}, Interval: "5s", } rawQuery, err := builder.Build(query, queryContext) So(err, ShouldBeNil) So(rawQuery, ShouldEqual, `SELECT mean("value") FROM "cpu" WHERE "hostname" = 'server1' OR "hostname" = 'server2' AND time > now() - 5m GROUP BY time(5s), "datacenter" fill(null)`) }) Convey("can render time range", func() { query := Query{} builder := &QueryBuilder{} Convey("render from: 2h to now-1h", func() { query := Query{} queryContext := &tsdb.QueryContext{TimeRange: tsdb.NewTimeRange("2h", "now-1h")} So(builder.renderTimeFilter(&query, queryContext), ShouldEqual, "time > now() - 2h and time < now() - 1h") }) Convey("render from: 10m", func() { queryContext := &tsdb.QueryContext{TimeRange: tsdb.NewTimeRange("10m", "now")} So(builder.renderTimeFilter(&query, queryContext), ShouldEqual, "time > now() - 10m") }) }) Convey("can build query from raw query", func() { query := &Query{ Selects: []*Select{{*qp1, *qp2}}, Measurement: "cpu", Policy: "policy", GroupBy: []*QueryPart{groupBy1, groupBy3}, Interval: "10s", RawQuery: "Raw query", } rawQuery, err := builder.Build(query, queryContext) So(err, ShouldBeNil) So(rawQuery, ShouldEqual, `Raw query`) }) }) }
func TestInfluxdbQueryBuilder(t *testing.T) { Convey("Influxdb query builder", t, func() { qp1, _ := NewQueryPart("field", []string{"value"}) qp2, _ := NewQueryPart("mean", []string{}) groupBy1, _ := NewQueryPart("time", []string{"$interval"}) groupBy2, _ := NewQueryPart("tag", []string{"datacenter"}) groupBy3, _ := NewQueryPart("fill", []string{"null"}) tag1 := &Tag{Key: "hostname", Value: "server1", Operator: "="} tag2 := &Tag{Key: "hostname", Value: "server2", Operator: "=", Condition: "OR"} queryContext := &tsdb.QueryContext{ TimeRange: tsdb.NewTimeRange("5m", "now"), } Convey("can build simple query", func() { query := &Query{ Selects: []*Select{{*qp1, *qp2}}, Measurement: "cpu", Policy: "policy", GroupBy: []*QueryPart{groupBy1, groupBy3}, Interval: "10s", } rawQuery, err := query.Build(queryContext) So(err, ShouldBeNil) So(rawQuery, ShouldEqual, `SELECT mean("value") FROM "policy"."cpu" WHERE time > now() - 5m GROUP BY time(10s) fill(null)`) }) Convey("can build query with group bys", func() { query := &Query{ Selects: []*Select{{*qp1, *qp2}}, Measurement: "cpu", GroupBy: []*QueryPart{groupBy1, groupBy2, groupBy3}, Tags: []*Tag{tag1, tag2}, Interval: "5s", } rawQuery, err := query.Build(queryContext) So(err, ShouldBeNil) So(rawQuery, ShouldEqual, `SELECT mean("value") FROM "cpu" WHERE "hostname" = 'server1' OR "hostname" = 'server2' AND time > now() - 5m GROUP BY time(5s), "datacenter" fill(null)`) }) Convey("can render time range", func() { query := Query{} Convey("render from: 2h to now-1h", func() { query := Query{} queryContext := &tsdb.QueryContext{TimeRange: tsdb.NewTimeRange("2h", "now-1h")} So(query.renderTimeFilter(queryContext), ShouldEqual, "time > now() - 2h and time < now() - 1h") }) Convey("render from: 10m", func() { queryContext := &tsdb.QueryContext{TimeRange: tsdb.NewTimeRange("10m", "now")} So(query.renderTimeFilter(queryContext), ShouldEqual, "time > now() - 10m") }) }) Convey("can build query from raw query", func() { query := &Query{ Selects: []*Select{{*qp1, *qp2}}, Measurement: "cpu", Policy: "policy", GroupBy: []*QueryPart{groupBy1, groupBy3}, Interval: "10s", RawQuery: "Raw query", UseRawQuery: true, } rawQuery, err := query.Build(queryContext) So(err, ShouldBeNil) So(rawQuery, ShouldEqual, `Raw query`) }) Convey("can render normal tags without operator", func() { query := &Query{Tags: []*Tag{&Tag{Operator: "", Value: `value`, Key: "key"}}} So(strings.Join(query.renderTags(), ""), ShouldEqual, `"key" = 'value'`) }) Convey("can render regex tags without operator", func() { query := &Query{Tags: []*Tag{&Tag{Operator: "", Value: `/value/`, Key: "key"}}} So(strings.Join(query.renderTags(), ""), ShouldEqual, `"key" =~ /value/`) }) Convey("can render regex tags", func() { query := &Query{Tags: []*Tag{&Tag{Operator: "=~", Value: `/value/`, Key: "key"}}} So(strings.Join(query.renderTags(), ""), ShouldEqual, `"key" =~ /value/`) }) Convey("can render number tags", func() { query := &Query{Tags: []*Tag{&Tag{Operator: "=", Value: "10001", Key: "key"}}} So(strings.Join(query.renderTags(), ""), ShouldEqual, `"key" = '10001'`) }) Convey("can render numbers less then condition tags", func() { query := &Query{Tags: []*Tag{&Tag{Operator: "<", Value: "10001", Key: "key"}}} So(strings.Join(query.renderTags(), ""), ShouldEqual, `"key" < 10001`) }) Convey("can render number greather then condition tags", func() { query := &Query{Tags: []*Tag{&Tag{Operator: ">", Value: "10001", Key: "key"}}} So(strings.Join(query.renderTags(), ""), ShouldEqual, `"key" > 10001`) }) Convey("can render string tags", func() { query := &Query{Tags: []*Tag{&Tag{Operator: "=", Value: "value", Key: "key"}}} So(strings.Join(query.renderTags(), ""), ShouldEqual, `"key" = 'value'`) }) Convey("can render regular measurement", func() { query := &Query{Measurement: `apa`, Policy: "policy"} So(query.renderMeasurement(), ShouldEqual, ` FROM "policy"."apa"`) }) Convey("can render regexp measurement", func() { query := &Query{Measurement: `/apa/`, Policy: "policy"} So(query.renderMeasurement(), ShouldEqual, ` FROM "policy"./apa/`) }) }) }