Skip to content

Commit c77d72b

Browse files
committed
feat(prometheus): progress on new prometheus query editor, grafana#5117
1 parent a3ee388 commit c77d72b

File tree

6 files changed

+209
-55
lines changed

6 files changed

+209
-55
lines changed

public/app/plugins/datasource/prometheus/metric_find_query.js

Lines changed: 82 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -11,27 +11,33 @@ function (_) {
1111
}
1212

1313
PrometheusMetricFindQuery.prototype.process = function() {
14-
var label_values_regex = /^label_values\((?:(.+),\s*)?([a-zA-Z_][a-zA-Z0-9_]+)\)$/;
15-
var metric_names_regex = /^metrics\((.+)\)$/;
16-
var query_result_regex = /^query_result\((.+)\)$/;
17-
18-
var label_values_query = this.query.match(label_values_regex);
19-
if (label_values_query) {
20-
if (label_values_query[1]) {
21-
return this.labelValuesQuery(label_values_query[2], label_values_query[1]);
14+
var labelValuesRegex = /^label_values\((?:(.+),\s*)?([a-zA-Z_][a-zA-Z0-9_]+)\)$/;
15+
var metricNamesRegex = /^metrics\((.+)\)$/;
16+
var labelsRegex = /^labels\((.+)\)$/;
17+
var queryResultRegex = /^query_result\((.+)\)$/;
18+
19+
var labelsQuery = this.query.match(labelsRegex);
20+
if (labelsQuery) {
21+
return this.labelsQuery(labelsQuery[1]);
22+
}
23+
24+
var labelValuesQuery = this.query.match(labelValuesRegex);
25+
if (labelValuesQuery) {
26+
if (labelValuesQuery[1]) {
27+
return this.labelValuesQuery(labelValuesQuery[2], labelValuesQuery[1]);
2228
} else {
23-
return this.labelValuesQuery(label_values_query[2], null);
29+
return this.labelValuesQuery(labelValuesQuery[2], null);
2430
}
2531
}
2632

27-
var metric_names_query = this.query.match(metric_names_regex);
28-
if (metric_names_query) {
29-
return this.metricNameQuery(metric_names_query[1]);
33+
var metricNamesQuery = this.query.match(metricNamesRegex);
34+
if (metricNamesQuery) {
35+
return this.metricNameQuery(metricNamesQuery[1]);
3036
}
3137

32-
var query_result_query = this.query.match(query_result_regex);
33-
if (query_result_query) {
34-
return this.queryResultQuery(query_result_query[1]);
38+
var queryResultQuery = this.query.match(queryResultRegex);
39+
if (queryResultQuery) {
40+
return this.queryResultQuery(queryResultQuery[1]);
3541
}
3642

3743
// if query contains full metric name, return metric name and label list
@@ -67,45 +73,71 @@ function (_) {
6773
}
6874
};
6975

76+
PrometheusMetricFindQuery.prototype.labelsQuery = function(metric) {
77+
var url;
78+
79+
url = '/api/v1/series?match[]=' + encodeURIComponent(metric)
80+
+ '&start=' + (this.range.from.valueOf() / 1000)
81+
+ '&end=' + (this.range.to.valueOf() / 1000);
82+
83+
return this.datasource._request('GET', url)
84+
.then(function(result) {
85+
var tags = {};
86+
_.each(result.data.data, function(metric) {
87+
_.each(metric, function(value, key) {
88+
if (key === "__name__") {
89+
return;
90+
}
91+
92+
tags[key] = key;
93+
});
94+
});
95+
96+
return _.map(tags, function(value) {
97+
return {text: value, value: value};
98+
});
99+
});
100+
};
101+
70102
PrometheusMetricFindQuery.prototype.metricNameQuery = function(metricFilterPattern) {
71103
var url = '/api/v1/label/__name__/values';
72104

73105
return this.datasource._request('GET', url)
74-
.then(function(result) {
75-
return _.chain(result.data.data)
76-
.filter(function(metricName) {
77-
var r = new RegExp(metricFilterPattern);
78-
return r.test(metricName);
79-
})
80-
.map(function(matchedMetricName) {
81-
return {
82-
text: matchedMetricName,
83-
expandable: true
84-
};
85-
})
86-
.value();
87-
});
106+
.then(function(result) {
107+
return _.chain(result.data.data)
108+
.filter(function(metricName) {
109+
var r = new RegExp(metricFilterPattern);
110+
return r.test(metricName);
111+
})
112+
.map(function(matchedMetricName) {
113+
return {
114+
text: matchedMetricName,
115+
expandable: true
116+
};
117+
})
118+
.value();
119+
});
88120
};
89121

90122
PrometheusMetricFindQuery.prototype.queryResultQuery = function(query) {
91123
var url = '/api/v1/query?query=' + encodeURIComponent(query) + '&time=' + (this.range.to.valueOf() / 1000);
92124

93125
return this.datasource._request('GET', url)
94-
.then(function(result) {
95-
return _.map(result.data.data.result, function(metricData) {
96-
var text = metricData.metric.__name__ || '';
97-
delete metricData.metric.__name__;
98-
text += '{' +
99-
_.map(metricData.metric, function(v, k) { return k + '="' + v + '"'; }).join(',') +
100-
'}';
101-
text += ' ' + metricData.value[1] + ' ' + metricData.value[0] * 1000;
102-
103-
return {
104-
text: text,
105-
expandable: true
106-
};
126+
.then(function(result) {
127+
return _.map(result.data.data.result, function(metricData) {
128+
var text = metricData.metric.__name__ || '';
129+
delete metricData.metric.__name__;
130+
text += '{' +
131+
_.map(metricData.metric, function(v, k) { return k + '="' + v + '"'; }).join(',') +
132+
'}';
133+
text += ' ' + metricData.value[1] + ' ' + metricData.value[0] * 1000;
134+
135+
return {
136+
text: text,
137+
expandable: true
138+
};
139+
});
107140
});
108-
});
109141
};
110142

111143
PrometheusMetricFindQuery.prototype.metricNameAndLabelsQuery = function(query) {
@@ -115,14 +147,14 @@ function (_) {
115147

116148
var self = this;
117149
return this.datasource._request('GET', url)
118-
.then(function(result) {
119-
return _.map(result.data.data, function(metric) {
120-
return {
121-
text: self.datasource.getOriginalMetricName(metric),
122-
expandable: true
123-
};
150+
.then(function(result) {
151+
return _.map(result.data.data, function(metric) {
152+
return {
153+
text: self.datasource.getOriginalMetricName(metric),
154+
expandable: true
155+
};
156+
});
124157
});
125-
});
126158
};
127159

128160
return PrometheusMetricFindQuery;

public/app/plugins/datasource/prometheus/partials/query.editor.html

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1-
<query-editor-row query-ctrl="ctrl" can-collapse="false">
2-
<div class="gf-form-inline">
1+
<query-editor-row query-ctrl="ctrl" can-collapse="false" has-text-edit-mode="true">
2+
3+
<div class="gf-form" ng-if="!ctrl.target.editorMode">
4+
<input type="text" class="gf-form-input" ng-model="ctrl.target.expr" spellcheck="false" ng-blur="ctrl.refresh()"></input>
5+
</div>
6+
7+
<div class="gf-form-inline" ng-if="ctrl.target.editorMode">
38
<div class="gf-form">
49
<label class="gf-form-label width-8 query-keyword">Query</label>
510
<metric-segment segment="ctrl.metricSegment" get-options="ctrl.getMetricOptions()" on-change="ctrl.queryChanged()"></metric-segment>

public/app/plugins/datasource/prometheus/prom_query.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ import {
22
QueryPartDef,
33
QueryPart,
44
functionRenderer,
5-
suffixRenderer,
65
identityRenderer,
76
quotedIdentityRenderer,
87
} from 'app/core/components/query_part/query_part';
@@ -12,6 +11,7 @@ import _ from 'lodash';
1211
var index = [];
1312
var categories = {
1413
Functions: [],
14+
GroupBy: [],
1515
};
1616

1717
export class PromQuery {
@@ -29,6 +29,7 @@ export class PromQuery {
2929
this.target.expr = this.target.expr || '';
3030
this.target.intervalFactor = this.target.intervalFactor || 2;
3131
this.target.functions = this.target.functions || [];
32+
this.target.editorMode = this.target.editorMode || true;
3233

3334
this.templateSrv = templateSrv;
3435
this.scopedVars = scopedVars;
@@ -80,6 +81,10 @@ function addFunctionStrategy(model, partModel) {
8081
model.target.functions.push(partModel.part);
8182
}
8283

84+
function groupByLabelRenderer(part, innerExpr) {
85+
return innerExpr + ' by(' + part.params.join(',') + ')';
86+
}
87+
8388
register({
8489
type: 'rate',
8590
addStrategy: addFunctionStrategy,
@@ -89,6 +94,26 @@ register({
8994
renderer: functionRenderer,
9095
});
9196

97+
register({
98+
type: 'sum',
99+
addStrategy: addFunctionStrategy,
100+
category: categories.Functions,
101+
params: [],
102+
defaultParams: [],
103+
renderer: functionRenderer,
104+
});
105+
106+
register({
107+
type: 'by',
108+
addStrategy: addFunctionStrategy,
109+
category: categories.Functions,
110+
params: [
111+
{name: "label", type: "string", dynamicLookup: true}
112+
],
113+
defaultParams: [],
114+
renderer: groupByLabelRenderer,
115+
});
116+
92117
export function getQueryPartCategories() {
93118
return categories;
94119
}

public/app/plugins/datasource/prometheus/query_ctrl.ts

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ class PrometheusQueryCtrl extends QueryCtrl {
2020
linkToPrometheus: any;
2121

2222
/** @ngInject */
23-
constructor($scope, $injector, private templateSrv, private uiSegmentSrv) {
23+
constructor($scope, $injector, private templateSrv, private uiSegmentSrv, private $rootScope) {
2424
super($scope, $injector);
2525

2626
this.query = new PromQuery(this.target, templateSrv);
@@ -58,6 +58,39 @@ class PrometheusQueryCtrl extends QueryCtrl {
5858
this.panelCtrl.refresh();
5959
}
6060

61+
getPartOptions(part) {
62+
if (part.def.type === 'by') {
63+
return this.datasource.metricFindQuery('labels(' + this.target.metric + ')')
64+
.then(this.transformToSegments(true))
65+
.catch(this.handleQueryError.bind(true));
66+
}
67+
}
68+
69+
partUpdated(part) {
70+
this.target.expr = this.query.render();
71+
this.panelCtrl.refresh();
72+
}
73+
74+
handleQueryError(err) {
75+
this.$rootScope.appEvent('alert-error', ['Query failed', err.message]);
76+
}
77+
78+
transformToSegments(addTemplateVars) {
79+
return (results) => {
80+
var segments = _.map(results, segment => {
81+
return this.uiSegmentSrv.newSegment({ value: segment.text, expandable: segment.expandable });
82+
});
83+
84+
if (addTemplateVars) {
85+
for (let variable of this.templateSrv.variables) {
86+
segments.unshift(this.uiSegmentSrv.newSegment({ type: 'template', value: '/^$' + variable.name + '$/', expandable: true }));
87+
}
88+
}
89+
90+
return segments;
91+
};
92+
}
93+
6194
getMetricOptions() {
6295
return this.datasource.performSuggestQuery('').then(res => {
6396
return _.map(res, metric => {
@@ -68,6 +101,13 @@ class PrometheusQueryCtrl extends QueryCtrl {
68101

69102
queryChanged() {
70103
this.target.metric = this.metricSegment.value;
104+
this.target.expr = this.query.render();
105+
this.refresh();
106+
}
107+
108+
toggleEditorMode() {
109+
this.target.expr = this.query.render(false);
110+
this.target.editorMode = !this.target.editorMode;
71111
}
72112

73113
refreshMetricData() {

0 commit comments

Comments
 (0)