mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
feat(elasticsearch): metric response handling and processsing now supports alias patterns, {{term field name}} and {{metric}} now works, #1034
This commit is contained in:
parent
2aa695fb66
commit
572a80d1d1
@ -11,8 +11,8 @@ function (_) {
|
||||
|
||||
// This is quite complex
|
||||
// neeed to recurise down the nested buckets to build series
|
||||
ElasticResponse.prototype.processBuckets = function(aggs, target, series, level) {
|
||||
var value, metric, i, y, bucket, aggDef, esAgg, nestedSeries;
|
||||
ElasticResponse.prototype.processBuckets = function(aggs, target, seriesList, level, props) {
|
||||
var value, metric, i, y, bucket, aggDef, esAgg, newSeries;
|
||||
|
||||
aggDef = target.bucketAggs[level];
|
||||
esAgg = aggs[aggDef.id];
|
||||
@ -20,9 +20,9 @@ function (_) {
|
||||
if (level < target.bucketAggs.length - 1) {
|
||||
for (i = 0; i < esAgg.buckets.length; i++) {
|
||||
bucket = esAgg.buckets[i];
|
||||
nestedSeries = {prop: {key: bucket.key, field: aggDef.field}, series: []};
|
||||
series.push(nestedSeries);
|
||||
this.processBuckets(bucket, target, nestedSeries.series, level+1);
|
||||
props = _.clone(props);
|
||||
props[aggDef.field] = bucket.key;
|
||||
this.processBuckets(bucket, target, seriesList, level+1, props);
|
||||
}
|
||||
return;
|
||||
}
|
||||
@ -32,54 +32,86 @@ function (_) {
|
||||
|
||||
switch(metric.type) {
|
||||
case 'count': {
|
||||
var countSeries = { datapoints: [], metric: 'count'};
|
||||
newSeries = { datapoints: [], metric: 'count', props: props};
|
||||
for (i = 0; i < esAgg.buckets.length; i++) {
|
||||
bucket = esAgg.buckets[i];
|
||||
value = bucket.doc_count;
|
||||
countSeries.datapoints.push([value, bucket.key]);
|
||||
newSeries.datapoints.push([value, bucket.key]);
|
||||
}
|
||||
series.push(countSeries);
|
||||
seriesList.push(newSeries);
|
||||
break;
|
||||
}
|
||||
case 'percentiles': {
|
||||
// for (i = 0; i < esAgg.buckets.length; i++) {
|
||||
// bucket = esAgg.buckets[i];
|
||||
// var values = bucket[metric.id].values;
|
||||
// for (var prop in values) {
|
||||
// addMetricPoint(seriesName + ' ' + prop, values[prop], bucket.key);
|
||||
// }
|
||||
// }
|
||||
if (esAgg.buckets.length === 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
var firstBucket = esAgg.buckets[0];
|
||||
var percentiles = firstBucket[metric.id].values;
|
||||
|
||||
for (var percentileName in percentiles) {
|
||||
newSeries = {datapoints: [], metric: 'p' + percentileName, props: props};
|
||||
|
||||
for (i = 0; i < esAgg.buckets.length; i++) {
|
||||
bucket = esAgg.buckets[i];
|
||||
var values = bucket[metric.id].values;
|
||||
newSeries.datapoints.push([values[percentileName], bucket.key]);
|
||||
}
|
||||
seriesList.push(newSeries);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case 'extended_stats': {
|
||||
// var stats = bucket[metric.id];
|
||||
// stats.std_deviation_bounds_upper = stats.std_deviation_bounds.upper;
|
||||
// stats.std_deviation_bounds_lower = stats.std_deviation_bounds.lower;
|
||||
//
|
||||
// for (var statName in metric.meta) {
|
||||
// if (metric.meta[statName]) {
|
||||
// addMetricPoint(seriesName + ' ' + statName, stats[statName], bucket.key);
|
||||
// }
|
||||
// }
|
||||
for (var statName in metric.meta) {
|
||||
if (!metric.meta[statName]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
newSeries = {datapoints: [], metric: statName, props: props};
|
||||
|
||||
for (i = 0; i < esAgg.buckets.length; i++) {
|
||||
bucket = esAgg.buckets[i];
|
||||
var stats = bucket[metric.id];
|
||||
|
||||
// add stats that are in nested obj to top level obj
|
||||
stats.std_deviation_bounds_upper = stats.std_deviation_bounds.upper;
|
||||
stats.std_deviation_bounds_lower = stats.std_deviation_bounds.lower;
|
||||
|
||||
newSeries.datapoints.push([stats[statName], bucket.key]);
|
||||
}
|
||||
|
||||
seriesList.push(newSeries);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
var newSeries = { datapoints: [], metric: metric.type + ' ' + metric.field };
|
||||
newSeries = { datapoints: [], metric: metric.type + ' ' + metric.field, props: props};
|
||||
for (i = 0; i < esAgg.buckets.length; i++) {
|
||||
bucket = esAgg.buckets[i];
|
||||
value = bucket[metric.id].value;
|
||||
newSeries.datapoints.push([value, bucket.key]);
|
||||
}
|
||||
series.push(newSeries);
|
||||
seriesList.push(newSeries);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ElasticResponse.prototype._getSeriesName = function(props, metric, alias) {
|
||||
if (alias) {
|
||||
return alias;
|
||||
ElasticResponse.prototype._getSeriesName = function(props, metric, target, metricTypeCount) {
|
||||
if (target.alias) {
|
||||
var regex = /\{\{([\s\S]+?)\}\}/g;
|
||||
|
||||
return target.alias.replace(regex, function(match, g1, g2) {
|
||||
var group = g1 || g2;
|
||||
|
||||
if (props[group]) { return props[group]; }
|
||||
if (group === 'metric') { return metric; }
|
||||
|
||||
return match;
|
||||
});
|
||||
}
|
||||
|
||||
var propKeys = _.keys(props);
|
||||
@ -92,31 +124,15 @@ function (_) {
|
||||
name += props[propName] + ' ';
|
||||
}
|
||||
|
||||
if (propKeys.length === 1) {
|
||||
if (metricTypeCount === 1) {
|
||||
return name.trim();
|
||||
}
|
||||
|
||||
return name.trim() + ' ' + metric;
|
||||
};
|
||||
|
||||
ElasticResponse.prototype._collectSeriesFromTree = function(seriesTree, props, seriesList, alias) {
|
||||
console.log('props: ', props);
|
||||
|
||||
for (var i = 0; i < seriesTree.length; i++) {
|
||||
var series = seriesTree[i];
|
||||
if (series.datapoints) {
|
||||
series.target = this._getSeriesName(props, series.metric, alias);
|
||||
seriesList.push(series);
|
||||
} else {
|
||||
props = _.clone(props);
|
||||
props[series.prop.field] = series.prop.key;
|
||||
this._collectSeriesFromTree(series.series, props, seriesList);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ElasticResponse.prototype.getTimeSeries = function() {
|
||||
var series = [];
|
||||
var seriesList = [];
|
||||
|
||||
for (var i = 0; i < this.response.responses.length; i++) {
|
||||
var response = this.response.responses[i];
|
||||
@ -126,13 +142,20 @@ function (_) {
|
||||
|
||||
var aggregations = response.aggregations;
|
||||
var target = this.targets[i];
|
||||
var seriesTree = [];
|
||||
var tmpSeriesList = [];
|
||||
|
||||
this.processBuckets(aggregations, target, seriesTree, 0, '');
|
||||
this._collectSeriesFromTree(seriesTree, {}, series, '');
|
||||
this.processBuckets(aggregations, target, tmpSeriesList, 0, {});
|
||||
|
||||
var metricTypeCount = _.uniq(_.pluck(tmpSeriesList, 'metric')).length;
|
||||
|
||||
for (var y = 0; y < tmpSeriesList.length; y++) {
|
||||
var series= tmpSeriesList[y];
|
||||
series.target = this._getSeriesName(series.props, series.metric, target, metricTypeCount);
|
||||
seriesList.push(series);
|
||||
}
|
||||
}
|
||||
|
||||
return { data: series };
|
||||
return { data: seriesList };
|
||||
};
|
||||
|
||||
return ElasticResponse;
|
||||
|
@ -27,7 +27,6 @@ function (angular, _, queryDef) {
|
||||
$scope.validateModel = function() {
|
||||
$scope.isFirst = $scope.index === 0;
|
||||
$scope.isSingle = metricAggs.length === 1;
|
||||
|
||||
$scope.settingsLinkText = '';
|
||||
|
||||
if (!$scope.agg.field) {
|
||||
|
@ -47,8 +47,13 @@
|
||||
<li>
|
||||
<input type="text" class="tight-form-input" style="width: 345px;" ng-model="target.query" spellcheck='false' placeholder="Lucence query" ng-blur="get_data()">
|
||||
</li>
|
||||
<li class="tight-form-item query-keyword">
|
||||
Alias
|
||||
</li>
|
||||
<li>
|
||||
<input type="text" class="tight-form-input" style="width: 245px;" ng-model="target.alias" spellcheck='false' placeholder="alias patterns (empty = auto)" ng-blur="get_data()">
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="clearfix"></div>
|
||||
|
||||
<div style="padding: 10px" ng-if="target.rawQuery">
|
||||
@ -57,7 +62,6 @@
|
||||
</div>
|
||||
|
||||
<div ng-hide="target.rawQuery">
|
||||
|
||||
<div ng-repeat="agg in target.metrics">
|
||||
<elastic-metric-agg
|
||||
target="target" index="$index"
|
||||
|
@ -105,25 +105,5 @@ function (_) {
|
||||
return list;
|
||||
};
|
||||
|
||||
p.createNameForSeries = function(seriesName, groupByColValue) {
|
||||
var regex = /\$(\w+)/g;
|
||||
var segments = seriesName.split('.');
|
||||
|
||||
return this.alias.replace(regex, function(match, group) {
|
||||
if (group === 's') {
|
||||
return seriesName;
|
||||
}
|
||||
else if (group === 'g') {
|
||||
return groupByColValue;
|
||||
}
|
||||
var index = parseInt(group);
|
||||
if (_.isNumber(index) && index < segments.length) {
|
||||
return segments[index];
|
||||
}
|
||||
return match;
|
||||
});
|
||||
|
||||
};
|
||||
|
||||
return InfluxSeries;
|
||||
});
|
||||
|
@ -94,7 +94,7 @@ define([
|
||||
|
||||
});
|
||||
|
||||
describe('single group by query', function() {
|
||||
describe('single group by query one metric', function() {
|
||||
var result;
|
||||
|
||||
beforeEach(function() {
|
||||
@ -145,7 +145,60 @@ define([
|
||||
});
|
||||
});
|
||||
|
||||
describe.skip('with percentiles ', function() {
|
||||
describe('single group by query two metrics', function() {
|
||||
var result;
|
||||
|
||||
beforeEach(function() {
|
||||
targets = [{
|
||||
refId: 'A',
|
||||
metrics: [{type: 'count', id: '1'}, {type: 'avg', field: '@value', id: '4'}],
|
||||
bucketAggs: [{type: 'terms', field: 'host', id: '2'}, {type: 'date_histogram', field: '@timestamp', id: '3'}],
|
||||
}];
|
||||
response = {
|
||||
responses: [{
|
||||
aggregations: {
|
||||
"2": {
|
||||
buckets: [
|
||||
{
|
||||
"3": {
|
||||
buckets: [
|
||||
{ "4": {value: 10}, doc_count: 1, key: 1000},
|
||||
{ "4": {value: 12}, doc_count: 3, key: 2000}
|
||||
]
|
||||
},
|
||||
doc_count: 4,
|
||||
key: 'server1',
|
||||
},
|
||||
{
|
||||
"3": {
|
||||
buckets: [
|
||||
{ "4": {value: 20}, doc_count: 1, key: 1000},
|
||||
{ "4": {value: 32}, doc_count: 3, key: 2000}
|
||||
]
|
||||
},
|
||||
doc_count: 10,
|
||||
key: 'server2',
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
}]
|
||||
};
|
||||
|
||||
result = new ElasticResponse(targets, response).getTimeSeries();
|
||||
});
|
||||
|
||||
it('should return 2 series', function() {
|
||||
expect(result.data.length).to.be(4);
|
||||
expect(result.data[0].datapoints.length).to.be(2);
|
||||
expect(result.data[0].target).to.be('server1 count');
|
||||
expect(result.data[1].target).to.be('server1 avg @value');
|
||||
expect(result.data[2].target).to.be('server2 count');
|
||||
expect(result.data[3].target).to.be('server2 avg @value');
|
||||
});
|
||||
});
|
||||
|
||||
describe('with percentiles ', function() {
|
||||
var result;
|
||||
|
||||
beforeEach(function() {
|
||||
@ -181,22 +234,22 @@ define([
|
||||
it('should return 2 series', function() {
|
||||
expect(result.data.length).to.be(2);
|
||||
expect(result.data[0].datapoints.length).to.be(2);
|
||||
expect(result.data[0].target).to.be('75');
|
||||
expect(result.data[1].target).to.be('90');
|
||||
expect(result.data[0].target).to.be('p75');
|
||||
expect(result.data[1].target).to.be('p90');
|
||||
expect(result.data[0].datapoints[0][0]).to.be(3.3);
|
||||
expect(result.data[0].datapoints[0][1]).to.be(1000);
|
||||
expect(result.data[1].datapoints[1][0]).to.be(4.5);
|
||||
});
|
||||
});
|
||||
|
||||
describe.skip('with extended_stats ', function() {
|
||||
describe('with extended_stats', function() {
|
||||
var result;
|
||||
|
||||
beforeEach(function() {
|
||||
targets = [{
|
||||
refId: 'A',
|
||||
metrics: [{type: 'extended_stats', meta: {max: true, std_deviation_bounds_upper: true}, id: '1'}],
|
||||
bucketAggs: [{type: 'date_histogram', id: '3'}],
|
||||
bucketAggs: [{type: 'terms', field: 'host', id: '3'}, {type: 'date_histogram', id: '4'}],
|
||||
}];
|
||||
response = {
|
||||
responses: [{
|
||||
@ -204,15 +257,25 @@ define([
|
||||
"3": {
|
||||
buckets: [
|
||||
{
|
||||
"1": {max: 10.2, min: 5.5, std_deviation_bounds: {upper: 3, lower: -2}},
|
||||
doc_count: 10,
|
||||
key: 1000
|
||||
key: 'server1',
|
||||
"4": {
|
||||
buckets: [{
|
||||
"1": {max: 10.2, min: 5.5, std_deviation_bounds: {upper: 3, lower: -2}},
|
||||
doc_count: 10,
|
||||
key: 1000
|
||||
}]
|
||||
}
|
||||
},
|
||||
{
|
||||
"1": {max: 7.2, min: 3.5, std_deviation_bounds: {upper: 4, lower: -1}},
|
||||
doc_count: 15,
|
||||
key: 2000
|
||||
}
|
||||
key: 'server2',
|
||||
"4": {
|
||||
buckets: [{
|
||||
"1": {max: 10.2, min: 5.5, std_deviation_bounds: {upper: 3, lower: -2}},
|
||||
doc_count: 10,
|
||||
key: 1000
|
||||
}]
|
||||
}
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
@ -222,28 +285,25 @@ define([
|
||||
result = new ElasticResponse(targets, response).getTimeSeries();
|
||||
});
|
||||
|
||||
it('should return 2 series', function() {
|
||||
expect(result.data.length).to.be(2);
|
||||
expect(result.data[0].datapoints.length).to.be(2);
|
||||
expect(result.data[0].target).to.be('max');
|
||||
expect(result.data[1].target).to.be('std_deviation_bounds_upper');
|
||||
it('should return 4 series', function() {
|
||||
expect(result.data.length).to.be(4);
|
||||
expect(result.data[0].datapoints.length).to.be(1);
|
||||
expect(result.data[0].target).to.be('server1 max');
|
||||
expect(result.data[1].target).to.be('server1 std_deviation_bounds_upper');
|
||||
|
||||
expect(result.data[0].datapoints[0][0]).to.be(10.2);
|
||||
expect(result.data[0].datapoints[1][0]).to.be(7.2);
|
||||
|
||||
expect(result.data[1].datapoints[0][0]).to.be(3);
|
||||
expect(result.data[1].datapoints[1][0]).to.be(4);
|
||||
});
|
||||
});
|
||||
|
||||
describe.skip('single group by with alias pattern', function() {
|
||||
describe('single group by with alias pattern', function() {
|
||||
var result;
|
||||
|
||||
beforeEach(function() {
|
||||
targets = [{
|
||||
refId: 'A',
|
||||
metrics: [{type: 'count', id: '1'}],
|
||||
alias: '[[_@host]] $_metric and!',
|
||||
alias: '{{@host}} {{metric}} and!',
|
||||
bucketAggs: [
|
||||
{type: 'terms', field: '@host', id: '2'},
|
||||
{type: 'date_histogram', field: '@timestamp', id: '3'}
|
||||
|
Loading…
Reference in New Issue
Block a user