Skip to content

Commit c897d39

Browse files
committed
feat(influxdb): add support for serie alias replacement
ref grafana#6510
1 parent 24a3a10 commit c897d39

File tree

4 files changed

+191
-54
lines changed

4 files changed

+191
-54
lines changed

pkg/tsdb/influxdb/influxdb.go

Lines changed: 15 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"golang.org/x/net/context/ctxhttp"
1212

1313
"github.com/grafana/grafana/pkg/log"
14+
"github.com/grafana/grafana/pkg/setting"
1415
"github.com/grafana/grafana/pkg/tsdb"
1516
)
1617

@@ -50,9 +51,16 @@ func (e *InfluxDBExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice,
5051
return result.WithError(err)
5152
}
5253

53-
glog.Debug("Influxdb query", "raw query", query)
54+
rawQuery, err := e.QueryBuilder.Build(query, context)
55+
if err != nil {
56+
return result.WithError(err)
57+
}
58+
59+
if setting.Env == setting.DEV {
60+
glog.Debug("Influxdb query", "raw query", query)
61+
}
5462

55-
req, err := e.createRequest(query)
63+
req, err := e.createRequest(rawQuery)
5664
if err != nil {
5765
return result.WithError(err)
5866
}
@@ -77,28 +85,23 @@ func (e *InfluxDBExecutor) Execute(ctx context.Context, queries tsdb.QuerySlice,
7785
}
7886

7987
result.QueryResults = make(map[string]*tsdb.QueryResult)
80-
result.QueryResults["A"] = e.ResponseParser.Parse(&response)
88+
result.QueryResults["A"] = e.ResponseParser.Parse(&response, query)
8189

8290
return result
8391
}
8492

85-
func (e *InfluxDBExecutor) getQuery(queries tsdb.QuerySlice, context *tsdb.QueryContext) (string, error) {
93+
func (e *InfluxDBExecutor) getQuery(queries tsdb.QuerySlice, context *tsdb.QueryContext) (*Query, error) {
8694
for _, v := range queries {
8795

8896
query, err := e.QueryParser.Parse(v.Model, e.DataSourceInfo)
8997
if err != nil {
90-
return "", err
91-
}
92-
93-
rawQuery, err := e.QueryBuilder.Build(query, context)
94-
if err != nil {
95-
return "", err
98+
return nil, err
9699
}
97100

98-
return rawQuery, nil
101+
return query, nil
99102
}
100103

101-
return "", fmt.Errorf("query request contains no queries")
104+
return nil, fmt.Errorf("query request contains no queries")
102105
}
103106

104107
func (e *InfluxDBExecutor) createRequest(query string) (*http.Request, error) {

pkg/tsdb/influxdb/response_parser.go

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package influxdb
33
import (
44
"encoding/json"
55
"fmt"
6+
"regexp"
67
"strings"
78

89
"github.com/grafana/grafana/pkg/tsdb"
@@ -11,17 +12,25 @@ import (
1112

1213
type ResponseParser struct{}
1314

14-
func (rp *ResponseParser) Parse(response *Response) *tsdb.QueryResult {
15+
var (
16+
legendFormat *regexp.Regexp
17+
)
18+
19+
func init() {
20+
legendFormat = regexp.MustCompile(`\[\[(\w+?)*\]\]*|\$\s*(\w+?)*`)
21+
}
22+
23+
func (rp *ResponseParser) Parse(response *Response, query *Query) *tsdb.QueryResult {
1524
queryRes := tsdb.NewQueryResult()
1625

1726
for _, result := range response.Results {
18-
queryRes.Series = append(queryRes.Series, rp.transformRows(result.Series, queryRes)...)
27+
queryRes.Series = append(queryRes.Series, rp.transformRows(result.Series, queryRes, query)...)
1928
}
2029

2130
return queryRes
2231
}
2332

24-
func (rp *ResponseParser) transformRows(rows []Row, queryResult *tsdb.QueryResult) tsdb.TimeSeriesSlice {
33+
func (rp *ResponseParser) transformRows(rows []Row, queryResult *tsdb.QueryResult, query *Query) tsdb.TimeSeriesSlice {
2534
var result tsdb.TimeSeriesSlice
2635

2736
for _, row := range rows {
@@ -38,7 +47,7 @@ func (rp *ResponseParser) transformRows(rows []Row, queryResult *tsdb.QueryResul
3847
}
3948
}
4049
result = append(result, &tsdb.TimeSeries{
41-
Name: rp.formatSerieName(row, column),
50+
Name: rp.formatSerieName(row, column, query),
4251
Points: points,
4352
})
4453
}
@@ -47,7 +56,41 @@ func (rp *ResponseParser) transformRows(rows []Row, queryResult *tsdb.QueryResul
4756
return result
4857
}
4958

50-
func (rp *ResponseParser) formatSerieName(row Row, column string) string {
59+
func (rp *ResponseParser) formatSerieName(row Row, column string, query *Query) string {
60+
if query.Alias == "" {
61+
return rp.buildSerieNameFromQuery(row, column)
62+
}
63+
64+
result := legendFormat.ReplaceAllFunc([]byte(query.Alias), func(in []byte) []byte {
65+
aliasFormat := string(in)
66+
aliasFormat = strings.Replace(aliasFormat, "[[", "", 1)
67+
aliasFormat = strings.Replace(aliasFormat, "]]", "", 1)
68+
aliasFormat = strings.Replace(aliasFormat, "$", "", 1)
69+
70+
if aliasFormat == "m" || aliasFormat == "measurement" {
71+
return []byte(query.Measurement)
72+
}
73+
if aliasFormat == "col" {
74+
return []byte(column)
75+
}
76+
77+
if !strings.HasPrefix(aliasFormat, "tag_") {
78+
return in
79+
}
80+
81+
tagKey := strings.Replace(aliasFormat, "tag_", "", 1)
82+
tagValue, exist := row.Tags[tagKey]
83+
if exist {
84+
return []byte(tagValue)
85+
}
86+
87+
return in
88+
})
89+
90+
return string(result)
91+
}
92+
93+
func (rp *ResponseParser) buildSerieNameFromQuery(row Row, column string) string {
5194
var tags []string
5295

5396
for k, v := range row.Tags {

pkg/tsdb/influxdb/response_parser_test.go

Lines changed: 124 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -4,56 +4,147 @@ import (
44
"encoding/json"
55
"testing"
66

7+
"github.com/grafana/grafana/pkg/setting"
78
. "github.com/smartystreets/goconvey/convey"
89
)
910

1011
func TestInfluxdbResponseParser(t *testing.T) {
1112
Convey("Influxdb response parser", t, func() {
13+
Convey("Response parser", func() {
14+
parser := &ResponseParser{}
1215

13-
parser := &ResponseParser{}
14-
15-
response := &Response{
16-
Results: []Result{
17-
Result{
18-
Series: []Row{
19-
{
20-
Name: "cpu",
21-
Columns: []string{"time", "mean", "sum"},
22-
Tags: map[string]string{"datacenter": "America"},
23-
Values: [][]interface{}{
24-
{json.Number("111"), json.Number("222"), json.Number("333")},
25-
{json.Number("111"), json.Number("222"), json.Number("333")},
26-
{json.Number("111"), json.Number("null"), json.Number("333")},
16+
setting.NewConfigContext(&setting.CommandLineArgs{
17+
HomePath: "../../../",
18+
})
19+
20+
response := &Response{
21+
Results: []Result{
22+
Result{
23+
Series: []Row{
24+
{
25+
Name: "cpu",
26+
Columns: []string{"time", "mean", "sum"},
27+
Tags: map[string]string{"datacenter": "America"},
28+
Values: [][]interface{}{
29+
{json.Number("111"), json.Number("222"), json.Number("333")},
30+
{json.Number("111"), json.Number("222"), json.Number("333")},
31+
{json.Number("111"), json.Number("null"), json.Number("333")},
32+
},
2733
},
2834
},
2935
},
3036
},
31-
},
32-
}
37+
}
3338

34-
result := parser.Parse(response)
39+
query := &Query{}
3540

36-
Convey("can parse all series", func() {
37-
So(len(result.Series), ShouldEqual, 2)
38-
})
41+
result := parser.Parse(response, query)
3942

40-
Convey("can parse all points", func() {
41-
So(len(result.Series[0].Points), ShouldEqual, 3)
42-
So(len(result.Series[1].Points), ShouldEqual, 3)
43-
})
43+
Convey("can parse all series", func() {
44+
So(len(result.Series), ShouldEqual, 2)
45+
})
4446

45-
Convey("can parse multi row result", func() {
46-
So(result.Series[0].Points[1][0].Float64, ShouldEqual, float64(222))
47-
So(result.Series[1].Points[1][0].Float64, ShouldEqual, float64(333))
48-
})
47+
Convey("can parse all points", func() {
48+
So(len(result.Series[0].Points), ShouldEqual, 3)
49+
So(len(result.Series[1].Points), ShouldEqual, 3)
50+
})
4951

50-
Convey("can parse null points", func() {
51-
So(result.Series[0].Points[2][0].Valid, ShouldBeFalse)
52+
Convey("can parse multi row result", func() {
53+
So(result.Series[0].Points[1][0].Float64, ShouldEqual, float64(222))
54+
So(result.Series[1].Points[1][0].Float64, ShouldEqual, float64(333))
55+
})
56+
57+
Convey("can parse null points", func() {
58+
So(result.Series[0].Points[2][0].Valid, ShouldBeFalse)
59+
})
60+
61+
Convey("can format serie names", func() {
62+
So(result.Series[0].Name, ShouldEqual, "cpu.mean { datacenter: America }")
63+
So(result.Series[1].Name, ShouldEqual, "cpu.sum { datacenter: America }")
64+
})
5265
})
5366

54-
Convey("can format serie names", func() {
55-
So(result.Series[0].Name, ShouldEqual, "cpu.mean { datacenter: America }")
56-
So(result.Series[1].Name, ShouldEqual, "cpu.sum { datacenter: America }")
67+
Convey("Response parser with alias", func() {
68+
parser := &ResponseParser{}
69+
70+
response := &Response{
71+
Results: []Result{
72+
Result{
73+
Series: []Row{
74+
{
75+
Name: "cpu",
76+
Columns: []string{"time", "mean", "sum"},
77+
Tags: map[string]string{"datacenter": "America"},
78+
Values: [][]interface{}{
79+
{json.Number("111"), json.Number("222"), json.Number("333")},
80+
},
81+
},
82+
},
83+
},
84+
},
85+
}
86+
87+
Convey("$ alias", func() {
88+
Convey("simple alias", func() {
89+
query := &Query{Alias: "serie alias"}
90+
result := parser.Parse(response, query)
91+
92+
So(result.Series[0].Name, ShouldEqual, "serie alias")
93+
})
94+
95+
Convey("measurement alias", func() {
96+
query := &Query{Alias: "alias $m $measurement", Measurement: "10m"}
97+
result := parser.Parse(response, query)
98+
99+
So(result.Series[0].Name, ShouldEqual, "alias 10m 10m")
100+
})
101+
102+
Convey("column alias", func() {
103+
query := &Query{Alias: "alias $col", Measurement: "10m"}
104+
result := parser.Parse(response, query)
105+
106+
So(result.Series[0].Name, ShouldEqual, "alias mean")
107+
So(result.Series[1].Name, ShouldEqual, "alias sum")
108+
})
109+
110+
Convey("tag alias", func() {
111+
query := &Query{Alias: "alias $tag_datacenter"}
112+
result := parser.Parse(response, query)
113+
114+
So(result.Series[0].Name, ShouldEqual, "alias America")
115+
})
116+
})
117+
118+
Convey("[[]] alias", func() {
119+
Convey("simple alias", func() {
120+
query := &Query{Alias: "serie alias"}
121+
result := parser.Parse(response, query)
122+
123+
So(result.Series[0].Name, ShouldEqual, "serie alias")
124+
})
125+
126+
Convey("measurement alias", func() {
127+
query := &Query{Alias: "alias [[m]] [[measurement]]", Measurement: "10m"}
128+
result := parser.Parse(response, query)
129+
130+
So(result.Series[0].Name, ShouldEqual, "alias 10m 10m")
131+
})
132+
133+
Convey("column alias", func() {
134+
query := &Query{Alias: "alias [[col]]", Measurement: "10m"}
135+
result := parser.Parse(response, query)
136+
137+
So(result.Series[0].Name, ShouldEqual, "alias mean")
138+
So(result.Series[1].Name, ShouldEqual, "alias sum")
139+
})
140+
141+
Convey("tag alias", func() {
142+
query := &Query{Alias: "alias [[tag_datacenter]]"}
143+
result := parser.Parse(response, query)
144+
145+
So(result.Series[0].Name, ShouldEqual, "alias America")
146+
})
147+
})
57148
})
58149
})
59150
}

pkg/tsdb/prometheus/prometheus.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,14 @@ func NewPrometheusExecutor(dsInfo *tsdb.DataSourceInfo) tsdb.Executor {
2424
}
2525

2626
var (
27-
plog log.Logger
27+
plog log.Logger
28+
legendFormat *regexp.Regexp
2829
)
2930

3031
func init() {
3132
plog = log.New("tsdb.prometheus")
3233
tsdb.RegisterExecutor("prometheus", NewPrometheusExecutor)
34+
legendFormat = regexp.MustCompile(`\{\{\s*(.+?)\s*\}\}`)
3335
}
3436

3537
func (e *PrometheusExecutor) getClient() (prometheus.QueryAPI, error) {
@@ -79,13 +81,11 @@ func (e *PrometheusExecutor) Execute(ctx context.Context, queries tsdb.QuerySlic
7981
}
8082

8183
func formatLegend(metric pmodel.Metric, query *PrometheusQuery) string {
82-
reg, _ := regexp.Compile(`\{\{\s*(.+?)\s*\}\}`)
83-
8484
if query.LegendFormat == "" {
8585
return metric.String()
8686
}
8787

88-
result := reg.ReplaceAllFunc([]byte(query.LegendFormat), func(in []byte) []byte {
88+
result := legendFormat.ReplaceAllFunc([]byte(query.LegendFormat), func(in []byte) []byte {
8989
labelName := strings.Replace(string(in), "{{", "", 1)
9090
labelName = strings.Replace(labelName, "}}", "", 1)
9191
labelName = strings.TrimSpace(labelName)

0 commit comments

Comments
 (0)