grafana/public/app/plugins/datasource/prometheus/result_transformer.ts

229 lines
6.5 KiB
TypeScript
Raw Normal View History

2018-03-12 09:13:05 -05:00
import _ from 'lodash';
import TableModel from 'app/core/table_model';
export class ResultTransformer {
constructor(private templateSrv) {}
transform(response: any, options: any): any[] {
const prometheusResult = response.data.data.result;
2018-03-12 09:13:05 -05:00
if (options.format === 'table') {
return [
this.transformMetricDataToTable(
prometheusResult,
options.responseListLength,
options.refId,
options.valueWithRefId
),
];
2018-03-12 09:13:05 -05:00
} else if (options.format === 'heatmap') {
let seriesList = [];
prometheusResult.sort(sortSeriesByLabel);
for (const metricData of prometheusResult) {
2018-03-12 09:13:05 -05:00
seriesList.push(this.transformMetricData(metricData, options, options.start, options.end));
}
seriesList = this.transformToHistogramOverTime(seriesList);
return seriesList;
2018-03-12 09:13:05 -05:00
} else {
const seriesList = [];
for (const metricData of prometheusResult) {
2018-03-12 09:13:05 -05:00
if (response.data.data.resultType === 'matrix') {
seriesList.push(this.transformMetricData(metricData, options, options.start, options.end));
2018-03-12 09:13:05 -05:00
} else if (response.data.data.resultType === 'vector') {
seriesList.push(this.transformInstantMetricData(metricData, options));
2018-03-12 09:13:05 -05:00
}
}
return seriesList;
2018-03-12 09:13:05 -05:00
}
return [];
2018-03-12 09:13:05 -05:00
}
transformMetricData(metricData, options, start, end) {
const dps = [];
let metricLabel = null;
2018-03-12 09:13:05 -05:00
metricLabel = this.createMetricLabel(metricData.metric, options);
2018-03-12 09:13:05 -05:00
const stepMs = parseInt(options.step, 10) * 1000;
2018-03-12 09:13:05 -05:00
let baseTimestamp = start * 1000;
if (metricData.values === undefined) {
throw new Error('Prometheus heatmap error: data should be a time series');
}
for (const value of metricData.values) {
let dpValue = parseFloat(value[1]);
if (_.isNaN(dpValue)) {
dpValue = null;
2018-03-12 09:13:05 -05:00
}
const timestamp = parseFloat(value[0]) * 1000;
for (let t = baseTimestamp; t < timestamp; t += stepMs) {
dps.push([null, t]);
}
baseTimestamp = timestamp + stepMs;
dps.push([dpValue, timestamp]);
2018-03-12 09:13:05 -05:00
}
const endTimestamp = end * 1000;
for (let t = baseTimestamp; t <= endTimestamp; t += stepMs) {
dps.push([null, t]);
}
return {
datapoints: dps,
query: options.query,
target: metricLabel,
};
2018-03-12 09:13:05 -05:00
}
transformMetricDataToTable(md, resultCount: number, refId: string, valueWithRefId?: boolean) {
const table = new TableModel();
let i, j;
const metricLabels = {};
2018-03-12 09:13:05 -05:00
if (md.length === 0) {
return table;
}
// Collect all labels across all metrics
_.each(md, series => {
for (const label in series.metric) {
2018-03-12 09:13:05 -05:00
if (!metricLabels.hasOwnProperty(label)) {
metricLabels[label] = 1;
}
}
});
// Sort metric labels, create columns for them and record their index
const sortedLabels = _.keys(metricLabels).sort();
2018-03-12 09:13:05 -05:00
table.columns.push({ text: 'Time', type: 'time' });
_.each(sortedLabels, (label, labelIndex) => {
2018-03-12 09:13:05 -05:00
metricLabels[label] = labelIndex + 1;
table.columns.push({ text: label, filterable: !label.startsWith('__') });
2018-03-12 09:13:05 -05:00
});
const valueText = resultCount > 1 || valueWithRefId ? `Value #${refId}` : 'Value';
2018-03-12 09:13:05 -05:00
table.columns.push({ text: valueText });
// Populate rows, set value to empty string when label not present.
_.each(md, series => {
2018-03-12 09:13:05 -05:00
if (series.value) {
series.values = [series.value];
}
if (series.values) {
for (i = 0; i < series.values.length; i++) {
const values = series.values[i];
const reordered: any = [values[0] * 1000];
2018-03-12 09:13:05 -05:00
if (series.metric) {
for (j = 0; j < sortedLabels.length; j++) {
const label = sortedLabels[j];
2018-03-12 09:13:05 -05:00
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) {
const dps = [];
let metricLabel = null;
2018-03-12 09:13:05 -05:00
metricLabel = this.createMetricLabel(md.metric, options);
dps.push([parseFloat(md.value[1]), md.value[0] * 1000]);
return { target: metricLabel, datapoints: dps, labels: md.metric };
2018-03-12 09:13:05 -05:00
}
createMetricLabel(labelData, options) {
let label = '';
2018-03-12 09:13:05 -05:00
if (_.isUndefined(options) || _.isEmpty(options.legendFormat)) {
label = this.getOriginalMetricName(labelData);
} else {
label = this.renderTemplate(this.templateSrv.replace(options.legendFormat), labelData);
2018-03-12 09:13:05 -05:00
}
if (!label || label === '{}') {
label = options.query;
}
return label;
2018-03-12 09:13:05 -05:00
}
renderTemplate(aliasPattern, aliasData) {
const aliasRegex = /\{\{\s*(.+?)\s*\}\}/g;
return aliasPattern.replace(aliasRegex, (match, g1) => {
2018-03-12 09:13:05 -05:00
if (aliasData[g1]) {
return aliasData[g1];
}
return g1;
});
}
getOriginalMetricName(labelData) {
const metricName = labelData.__name__ || '';
2018-03-12 09:13:05 -05:00
delete labelData.__name__;
const labelPart = _.map(_.toPairs(labelData), label => {
2018-03-12 09:13:05 -05:00
return label[0] + '="' + label[1] + '"';
}).join(',');
return metricName + '{' + labelPart + '}';
}
transformToHistogramOverTime(seriesList) {
/* 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--) {
const topSeries = seriesList[i].datapoints;
const bottomSeries = seriesList[i - 1].datapoints;
if (!topSeries || !bottomSeries) {
throw new Error('Prometheus heatmap transform error: data should be a time series');
}
2018-03-12 09:13:05 -05:00
for (let j = 0; j < topSeries.length; j++) {
const bottomPoint = bottomSeries[j] || [0];
topSeries[j][0] -= bottomPoint[0];
2018-03-12 09:13:05 -05:00
}
}
return seriesList;
}
}
function sortSeriesByLabel(s1, s2): number {
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);
}