From aa7292fac61d328788e671935820a7a437d1c1b9 Mon Sep 17 00:00:00 2001 From: bergquist Date: Tue, 7 Feb 2017 17:45:36 +0100 Subject: [PATCH] mqe: adds support for wildcard and index aliases --- pkg/tsdb/mqe/httpClient.go | 2 +- pkg/tsdb/mqe/mqe.go | 1 + pkg/tsdb/mqe/response_parser.go | 100 +++++++++++++++++++++++---- pkg/tsdb/mqe/response_parser_test.go | 68 ++++++++++++++++-- pkg/tsdb/mqe/types.go | 2 + 5 files changed, 153 insertions(+), 20 deletions(-) diff --git a/pkg/tsdb/mqe/httpClient.go b/pkg/tsdb/mqe/httpClient.go index c4a7807f9cb..d8bf0888a35 100644 --- a/pkg/tsdb/mqe/httpClient.go +++ b/pkg/tsdb/mqe/httpClient.go @@ -86,7 +86,7 @@ func (e *apiClient) spawnWorker(ctx context.Context, id int, jobs chan QueryToSe return } - series, err := e.responseParser.Parse(resp, query.QueryRef) + series, err := e.responseParser.Parse(resp, query) if err != nil { errors <- err return diff --git a/pkg/tsdb/mqe/mqe.go b/pkg/tsdb/mqe/mqe.go index 5d5f0950de5..ae828e95c20 100644 --- a/pkg/tsdb/mqe/mqe.go +++ b/pkg/tsdb/mqe/mqe.go @@ -40,6 +40,7 @@ func init() { type QueryToSend struct { RawQuery string + Metric Metric QueryRef *Query } diff --git a/pkg/tsdb/mqe/response_parser.go b/pkg/tsdb/mqe/response_parser.go index d40106fc197..8cfa7d96cdd 100644 --- a/pkg/tsdb/mqe/response_parser.go +++ b/pkg/tsdb/mqe/response_parser.go @@ -4,9 +4,13 @@ import ( "encoding/json" "io/ioutil" "net/http" + "strconv" + "strings" "fmt" + "regexp" + "github.com/grafana/grafana/pkg/components/null" "github.com/grafana/grafana/pkg/log" "github.com/grafana/grafana/pkg/tsdb" @@ -18,6 +22,16 @@ func NewResponseParser() *ResponseParser { } } +var ( + indexAliasPattern *regexp.Regexp + wildcardAliasPattern *regexp.Regexp +) + +func init() { + indexAliasPattern = regexp.MustCompile(`\$(\d)`) + wildcardAliasPattern = regexp.MustCompile(`[*!]`) +} + type MQEResponse struct { Success bool `json:"success"` Name string `json:"name"` @@ -47,7 +61,7 @@ type ResponseParser struct { log log.Logger } -func (parser *ResponseParser) Parse(res *http.Response, queryRef *Query) ([]*tsdb.TimeSeries, error) { +func (parser *ResponseParser) Parse(res *http.Response, queryRef QueryToSend) ([]*tsdb.TimeSeries, error) { body, err := ioutil.ReadAll(res.Body) defer res.Body.Close() if err != nil { @@ -73,22 +87,14 @@ func (parser *ResponseParser) Parse(res *http.Response, queryRef *Query) ([]*tsd var series []*tsdb.TimeSeries for _, body := range data.Body { for _, mqeSerie := range body.Series { - namePrefix := "" - - //append predefined tags to seriename - for key, value := range mqeSerie.Tagset { - if key == "cluster" && queryRef.AddClusterToAlias { - namePrefix += value + " " - } + serie := &tsdb.TimeSeries{ + Tags: map[string]string{}, + Name: parser.formatLegend(body, mqeSerie, queryRef), } for key, value := range mqeSerie.Tagset { - if key == "host" && queryRef.AddHostToAlias { - namePrefix += value + " " - } + serie.Tags[key] = value } - serie := &tsdb.TimeSeries{Name: namePrefix + body.Name} - for i, value := range mqeSerie.Values { timestamp := body.TimeRange.Start + int64(i)*body.TimeRange.Resolution serie.Points = append(serie.Points, tsdb.NewTimePoint(value, float64(timestamp))) @@ -100,3 +106,71 @@ func (parser *ResponseParser) Parse(res *http.Response, queryRef *Query) ([]*tsd return series, nil } + +func (parser *ResponseParser) formatLegend(body MQEResponseSerie, mqeSerie MQESerie, queryToSend QueryToSend) string { + namePrefix := "" + + //append predefined tags to seriename + for key, value := range mqeSerie.Tagset { + if key == "cluster" && queryToSend.QueryRef.AddClusterToAlias { + namePrefix += value + " " + } + } + for key, value := range mqeSerie.Tagset { + if key == "host" && queryToSend.QueryRef.AddHostToAlias { + namePrefix += value + " " + } + } + + return namePrefix + parser.formatName(body, queryToSend) +} + +func (parser *ResponseParser) formatName(body MQEResponseSerie, queryToSend QueryToSend) string { + if indexAliasPattern.MatchString(queryToSend.Metric.Alias) { + return parser.indexAlias(body, queryToSend) + } + + if wildcardAliasPattern.MatchString(queryToSend.Metric.Metric) && wildcardAliasPattern.MatchString(queryToSend.Metric.Alias) { + return parser.wildcardAlias(body, queryToSend) + } + + return body.Name +} + +func (parser *ResponseParser) wildcardAlias(body MQEResponseSerie, queryToSend QueryToSend) string { + regString := strings.Replace(queryToSend.Metric.Metric, `*`, `(.*)`, 1) + reg, err := regexp.Compile(regString) + if err != nil { + return queryToSend.Metric.Alias + } + + matches := reg.FindAllStringSubmatch(queryToSend.RawQuery, -1) + + if len(matches) == 0 || len(matches[0]) < 2 { + return queryToSend.Metric.Alias + } + + return matches[0][1] +} + +func (parser *ResponseParser) indexAlias(body MQEResponseSerie, queryToSend QueryToSend) string { + queryNameParts := strings.Split(body.Name, `.`) + name := indexAliasPattern.ReplaceAllStringFunc(queryToSend.Metric.Alias, func(in string) string { + positionName := strings.TrimSpace(strings.Replace(in, "$", "", 1)) + + pos, err := strconv.Atoi(positionName) + if err != nil { + return "" + } + + for i, part := range queryNameParts { + if i == pos-1 { + return strings.TrimSpace(part) + } + } + + return "" + }) + + return strings.Replace(name, " ", ".", -1) +} diff --git a/pkg/tsdb/mqe/response_parser_test.go b/pkg/tsdb/mqe/response_parser_test.go index 63761841d97..237c25aa8fc 100644 --- a/pkg/tsdb/mqe/response_parser_test.go +++ b/pkg/tsdb/mqe/response_parser_test.go @@ -8,11 +8,12 @@ import ( "io/ioutil" + "github.com/grafana/grafana/pkg/components/null" . "github.com/smartystreets/goconvey/convey" ) var ( - dummieJson string + testJson string ) func TestMQEResponseParser(t *testing.T) { @@ -20,14 +21,17 @@ func TestMQEResponseParser(t *testing.T) { parser := NewResponseParser() Convey("Can parse response", func() { - queryRef := &Query{ - AddClusterToAlias: true, - AddHostToAlias: true, + queryRef := QueryToSend{ + QueryRef: &Query{ + AddClusterToAlias: true, + AddHostToAlias: true, + }, + Metric: Metric{Alias: ""}, } response := &http.Response{ StatusCode: 200, - Body: ioutil.NopCloser(strings.NewReader(dummieJson)), + Body: ioutil.NopCloser(strings.NewReader(testJson)), } res, err := parser.Parse(response, queryRef) So(err, ShouldBeNil) @@ -39,12 +43,64 @@ func TestMQEResponseParser(t *testing.T) { So(res[0].Points[i][0].Float64, ShouldEqual, i+1) So(res[0].Points[i][1].Float64, ShouldEqual, startTime+(i*30000)) } + + }) + + Convey("Can format legend", func() { + mqeSerie := MQESerie{ + Tagset: map[string]string{ + "cluster": "demoapp", + "host": "staples-lab-1", + }, + Values: []null.Float{null.NewFloat(3, true)}, + } + + Convey("with empty alias", func() { + serie := MQEResponseSerie{Name: "os.disk.sda3.weighted_io_time"} + queryRef := QueryToSend{ + QueryRef: &Query{ + AddClusterToAlias: true, + AddHostToAlias: true, + }, + Metric: Metric{Alias: ""}, + } + legend := parser.formatLegend(serie, mqeSerie, queryRef) + So(legend, ShouldEqual, "demoapp staples-lab-1 os.disk.sda3.weighted_io_time") + }) + + Convey("with index alias (ex $2 $3)", func() { + serie := MQEResponseSerie{Name: "os.disk.sda3.weighted_io_time"} + queryRef := QueryToSend{ + QueryRef: &Query{ + AddClusterToAlias: true, + AddHostToAlias: true, + }, + Metric: Metric{Alias: "$2 $3"}, + } + legend := parser.formatLegend(serie, mqeSerie, queryRef) + So(legend, ShouldEqual, "demoapp staples-lab-1 disk.sda3") + }) + + Convey("with wildcard alias", func() { + serie := MQEResponseSerie{Name: "os.disk.sda3.weighted_io_time", Query: "os.disk.*"} + + queryRef := QueryToSend{ + QueryRef: &Query{ + AddClusterToAlias: true, + AddHostToAlias: true, + }, + RawQuery: "os.disk.sda3.weighted_io_time", + Metric: Metric{Alias: "*", Metric: "os.disk.*.weighted_io_time"}, + } + legend := parser.formatLegend(serie, mqeSerie, queryRef) + So(legend, ShouldEqual, "demoapp staples-lab-1 sda3") + }) }) }) } func init() { - dummieJson = `{ + testJson = `{ "success": true, "name": "select", "body": [ diff --git a/pkg/tsdb/mqe/types.go b/pkg/tsdb/mqe/types.go index 4fa2c1d4e7f..0bd436ee9bd 100644 --- a/pkg/tsdb/mqe/types.go +++ b/pkg/tsdb/mqe/types.go @@ -53,6 +53,7 @@ func (q *Query) Build(availableSeries []string) ([]QueryToSend, error) { queriesToSend = append(queriesToSend, QueryToSend{ RawQuery: rawQuery, QueryRef: q, + Metric: metric, }) } else { m := strings.Replace(metric.Metric, "*", ".*", -1) @@ -70,6 +71,7 @@ func (q *Query) Build(availableSeries []string) ([]QueryToSend, error) { queriesToSend = append(queriesToSend, QueryToSend{ RawQuery: rawQuery, QueryRef: q, + Metric: metric, }) } }