|
|
@@ -3,7 +3,7 @@ import _ from 'lodash';
|
|
|
import kbn from 'app/core/utils/kbn';
|
|
|
import * as dateMath from 'app/core/utils/datemath';
|
|
|
import PrometheusMetricFindQuery from './metric_find_query';
|
|
|
-import TableModel from 'app/core/table_model';
|
|
|
+import { ResultTransformer } from './result_transformer';
|
|
|
|
|
|
function prometheusSpecialRegexEscape(value) {
|
|
|
return value.replace(/[\\^$*+?.()|[\]{}]/g, '\\\\$&');
|
|
|
@@ -20,6 +20,7 @@ export class PrometheusDatasource {
|
|
|
withCredentials: any;
|
|
|
metricsNameCache: any;
|
|
|
interval: string;
|
|
|
+ resultTransformer: ResultTransformer;
|
|
|
|
|
|
/** @ngInject */
|
|
|
constructor(instanceSettings, private $q, private backendSrv, private templateSrv, private timeSrv) {
|
|
|
@@ -32,6 +33,7 @@ export class PrometheusDatasource {
|
|
|
this.basicAuth = instanceSettings.basicAuth;
|
|
|
this.withCredentials = instanceSettings.withCredentials;
|
|
|
this.interval = instanceSettings.jsonData.timeInterval || '15s';
|
|
|
+ this.resultTransformer = new ResultTransformer(templateSrv);
|
|
|
}
|
|
|
|
|
|
_request(method, url, requestId?) {
|
|
|
@@ -73,7 +75,6 @@ export class PrometheusDatasource {
|
|
|
}
|
|
|
|
|
|
query(options) {
|
|
|
- var self = this;
|
|
|
var start = this.getPrometheusTime(options.range.from, false);
|
|
|
var end = this.getPrometheusTime(options.range.to, true);
|
|
|
var range = Math.ceil(end - start);
|
|
|
@@ -113,29 +114,17 @@ export class PrometheusDatasource {
|
|
|
throw response.error;
|
|
|
}
|
|
|
|
|
|
- let prometheusResult = response.data.data.result;
|
|
|
-
|
|
|
- if (activeTargets[index].format === 'table') {
|
|
|
- result.push(self.transformMetricDataToTable(prometheusResult, responseList.length, index));
|
|
|
- } else if (activeTargets[index].format === 'heatmap') {
|
|
|
- let seriesList = [];
|
|
|
- prometheusResult.sort(sortSeriesByLabel);
|
|
|
- for (let metricData of prometheusResult) {
|
|
|
- seriesList.push(
|
|
|
- self.transformMetricData(metricData, activeTargets[index], start, end, queries[index].step)
|
|
|
- );
|
|
|
- }
|
|
|
- seriesList = self.transformToHistogramOverTime(seriesList);
|
|
|
- result.push(...seriesList);
|
|
|
- } else {
|
|
|
- for (let metricData of prometheusResult) {
|
|
|
- if (response.data.data.resultType === 'matrix') {
|
|
|
- result.push(self.transformMetricData(metricData, activeTargets[index], start, end, queries[index].step));
|
|
|
- } else if (response.data.data.resultType === 'vector') {
|
|
|
- result.push(self.transformInstantMetricData(metricData, activeTargets[index]));
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
+ let transformerOptions = {
|
|
|
+ format: activeTargets[index].format,
|
|
|
+ step: queries[index].step,
|
|
|
+ legendFormat: activeTargets[index].legendFormat,
|
|
|
+ start: start,
|
|
|
+ end: end,
|
|
|
+ responseListLength: responseList.length,
|
|
|
+ responseIndex: index,
|
|
|
+ };
|
|
|
+
|
|
|
+ this.resultTransformer.transform(result, response, transformerOptions);
|
|
|
});
|
|
|
|
|
|
return { data: result };
|
|
|
@@ -276,9 +265,9 @@ export class PrometheusDatasource {
|
|
|
var event = {
|
|
|
annotation: annotation,
|
|
|
time: Math.floor(parseFloat(value[0])) * 1000,
|
|
|
- title: self.renderTemplate(titleFormat, series.metric),
|
|
|
+ title: self.resultTransformer.renderTemplate(titleFormat, series.metric),
|
|
|
tags: tags,
|
|
|
- text: self.renderTemplate(textFormat, series.metric),
|
|
|
+ text: self.resultTransformer.renderTemplate(textFormat, series.metric),
|
|
|
};
|
|
|
|
|
|
eventList.push(event);
|
|
|
@@ -296,145 +285,6 @@ export class PrometheusDatasource {
|
|
|
});
|
|
|
}
|
|
|
|
|
|
- transformMetricData(md, options, start, end, step) {
|
|
|
- var dps = [],
|
|
|
- metricLabel = null;
|
|
|
-
|
|
|
- metricLabel = this.createMetricLabel(md.metric, options);
|
|
|
-
|
|
|
- var stepMs = step * 1000;
|
|
|
- var baseTimestamp = start * 1000;
|
|
|
- for (let value of md.values) {
|
|
|
- var dp_value = parseFloat(value[1]);
|
|
|
- if (_.isNaN(dp_value)) {
|
|
|
- dp_value = null;
|
|
|
- }
|
|
|
-
|
|
|
- var timestamp = parseFloat(value[0]) * 1000;
|
|
|
- for (let t = baseTimestamp; t < timestamp; t += stepMs) {
|
|
|
- dps.push([null, t]);
|
|
|
- }
|
|
|
- baseTimestamp = timestamp + stepMs;
|
|
|
- dps.push([dp_value, timestamp]);
|
|
|
- }
|
|
|
-
|
|
|
- var endTimestamp = end * 1000;
|
|
|
- for (let t = baseTimestamp; t <= endTimestamp; t += stepMs) {
|
|
|
- dps.push([null, t]);
|
|
|
- }
|
|
|
-
|
|
|
- return { target: metricLabel, datapoints: dps };
|
|
|
- }
|
|
|
-
|
|
|
- transformMetricDataToTable(md, resultCount: number, resultIndex: number) {
|
|
|
- var table = new TableModel();
|
|
|
- var i, j;
|
|
|
- var metricLabels = {};
|
|
|
-
|
|
|
- if (md.length === 0) {
|
|
|
- return table;
|
|
|
- }
|
|
|
-
|
|
|
- // Collect all labels across all metrics
|
|
|
- _.each(md, function(series) {
|
|
|
- for (var label in series.metric) {
|
|
|
- if (!metricLabels.hasOwnProperty(label)) {
|
|
|
- metricLabels[label] = 1;
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- // Sort metric labels, create columns for them and record their index
|
|
|
- var sortedLabels = _.keys(metricLabels).sort();
|
|
|
- table.columns.push({ text: 'Time', type: 'time' });
|
|
|
- _.each(sortedLabels, function(label, labelIndex) {
|
|
|
- metricLabels[label] = labelIndex + 1;
|
|
|
- table.columns.push({ text: label });
|
|
|
- });
|
|
|
- let valueText = resultCount > 1 ? `Value #${String.fromCharCode(65 + resultIndex)}` : 'Value';
|
|
|
- table.columns.push({ text: valueText });
|
|
|
-
|
|
|
- // Populate rows, set value to empty string when label not present.
|
|
|
- _.each(md, function(series) {
|
|
|
- if (series.value) {
|
|
|
- series.values = [series.value];
|
|
|
- }
|
|
|
- if (series.values) {
|
|
|
- for (i = 0; i < series.values.length; i++) {
|
|
|
- var values = series.values[i];
|
|
|
- var reordered: any = [values[0] * 1000];
|
|
|
- if (series.metric) {
|
|
|
- for (j = 0; j < sortedLabels.length; j++) {
|
|
|
- var label = sortedLabels[j];
|
|
|
- if (series.metric.hasOwnProperty(label)) {
|
|
|
- reordered.push(series.metric[label]);
|
|
|
- } else {
|
|
|
- reordered.push('');
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- reordered.push(parseFloat(values[1]));
|
|
|
- table.rows.push(reordered);
|
|
|
- }
|
|
|
- }
|
|
|
- });
|
|
|
-
|
|
|
- return table;
|
|
|
- }
|
|
|
-
|
|
|
- transformInstantMetricData(md, options) {
|
|
|
- var dps = [],
|
|
|
- metricLabel = null;
|
|
|
- metricLabel = this.createMetricLabel(md.metric, options);
|
|
|
- dps.push([parseFloat(md.value[1]), md.value[0] * 1000]);
|
|
|
- return { target: metricLabel, datapoints: dps };
|
|
|
- }
|
|
|
-
|
|
|
- transformToHistogramOverTime(seriesList, options?) {
|
|
|
- /* t1 = timestamp1, t2 = timestamp2 etc.
|
|
|
- t1 t2 t3 t1 t2 t3
|
|
|
- le10 10 10 0 => 10 10 0
|
|
|
- le20 20 10 30 => 10 0 30
|
|
|
- le30 30 10 35 => 10 0 5
|
|
|
- */
|
|
|
- for (let i = seriesList.length - 1; i > 0; i--) {
|
|
|
- let topSeries = seriesList[i].datapoints;
|
|
|
- let bottomSeries = seriesList[i - 1].datapoints;
|
|
|
- for (let j = 0; j < topSeries.length; j++) {
|
|
|
- topSeries[j][0] -= bottomSeries[j][0];
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- return seriesList;
|
|
|
- }
|
|
|
-
|
|
|
- createMetricLabel(labelData, options) {
|
|
|
- if (_.isUndefined(options) || _.isEmpty(options.legendFormat)) {
|
|
|
- return this.getOriginalMetricName(labelData);
|
|
|
- }
|
|
|
-
|
|
|
- return this.renderTemplate(this.templateSrv.replace(options.legendFormat), labelData) || '{}';
|
|
|
- }
|
|
|
-
|
|
|
- renderTemplate(aliasPattern, aliasData) {
|
|
|
- var aliasRegex = /\{\{\s*(.+?)\s*\}\}/g;
|
|
|
- return aliasPattern.replace(aliasRegex, function(match, g1) {
|
|
|
- if (aliasData[g1]) {
|
|
|
- return aliasData[g1];
|
|
|
- }
|
|
|
- return g1;
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- getOriginalMetricName(labelData) {
|
|
|
- var metricName = labelData.__name__ || '';
|
|
|
- delete labelData.__name__;
|
|
|
- var labelPart = _.map(_.toPairs(labelData), function(label) {
|
|
|
- return label[0] + '="' + label[1] + '"';
|
|
|
- }).join(',');
|
|
|
- return metricName + '{' + labelPart + '}';
|
|
|
- }
|
|
|
-
|
|
|
getPrometheusTime(date, roundUp) {
|
|
|
if (_.isString(date)) {
|
|
|
date = dateMath.parse(date, roundUp);
|
|
|
@@ -442,33 +292,3 @@ export class PrometheusDatasource {
|
|
|
return Math.ceil(date.valueOf() / 1000);
|
|
|
}
|
|
|
}
|
|
|
-
|
|
|
-function sortSeriesByLabel(s1, s2) {
|
|
|
- let le1, le2;
|
|
|
-
|
|
|
- try {
|
|
|
- // fail if not integer. might happen with bad queries
|
|
|
- le1 = parseHistogramLabel(s1.metric.le);
|
|
|
- le2 = parseHistogramLabel(s2.metric.le);
|
|
|
- } catch (err) {
|
|
|
- console.log(err);
|
|
|
- return 0;
|
|
|
- }
|
|
|
-
|
|
|
- if (le1 > le2) {
|
|
|
- return 1;
|
|
|
- }
|
|
|
-
|
|
|
- if (le1 < le2) {
|
|
|
- return -1;
|
|
|
- }
|
|
|
-
|
|
|
- return 0;
|
|
|
-}
|
|
|
-
|
|
|
-function parseHistogramLabel(le: string): number {
|
|
|
- if (le === '+Inf') {
|
|
|
- return +Infinity;
|
|
|
- }
|
|
|
- return Number(le);
|
|
|
-}
|