Commit d7224c81 authored by Tim Zallmann's avatar Tim Zallmann

Merge branch 'multiple-query-prometheus-graphs' into 'master'

Enable multiple queries per Prometheus graph

See merge request gitlab-org/gitlab-ce!15201
parents 435fd9d0 2b9230af
Pipeline #13643860 failed with stages
in 103 minutes and 18 seconds
......@@ -138,7 +138,7 @@
renderAxesPaths() {
this.timeSeries = createTimeSeries(
this.graphData.queries[0],
this.graphData.queries,
this.graphWidth,
this.graphHeight,
this.graphHeightOffset,
......@@ -153,8 +153,9 @@
const axisYScale = d3.scale.linear()
.range([this.graphHeight - this.graphHeightOffset, 0]);
axisXScale.domain(d3.extent(this.timeSeries[0].values, d => d.time));
axisYScale.domain([0, d3.max(this.timeSeries[0].values.map(d => d.value))]);
const allValues = this.timeSeries.reduce((all, { values }) => all.concat(values), []);
axisXScale.domain(d3.extent(allValues, d => d.time));
axisYScale.domain([0, d3.max(allValues.map(d => d.value))]);
const xAxis = d3.svg.axis()
.scale(axisXScale)
......@@ -246,6 +247,7 @@
:key="index"
:generated-line-path="path.linePath"
:generated-area-path="path.areaPath"
:line-style="path.lineStyle"
:line-color="path.lineColor"
:area-color="path.areaColor"
/>
......
......@@ -79,7 +79,8 @@
},
formatMetricUsage(series) {
const value = series.values[this.currentDataIndex].value;
const value = series.values[this.currentDataIndex] &&
series.values[this.currentDataIndex].value;
if (isNaN(value)) {
return '-';
}
......@@ -92,6 +93,12 @@
}
return `${this.legendTitle} series ${index + 1} ${this.formatMetricUsage(series)}`;
},
strokeDashArray(type) {
if (type === 'dashed') return '6, 3';
if (type === 'dotted') return '3, 3';
return null;
},
},
mounted() {
this.$nextTick(() => {
......@@ -162,13 +169,15 @@
v-for="(series, index) in timeSeries"
:key="index"
:transform="translateLegendGroup(index)">
<rect
:fill="series.areaColor"
:width="measurements.legends.width"
:height="measurements.legends.height"
x="20"
:y="graphHeight - measurements.legendOffset">
</rect>
<line
:stroke="series.lineColor"
:stroke-width="measurements.legends.height"
:stroke-dasharray="strokeDashArray(series.lineStyle)"
:x1="measurements.legends.offsetX"
:x2="measurements.legends.offsetX + measurements.legends.width"
:y1="graphHeight - measurements.legends.offsetY"
:y2="graphHeight - measurements.legends.offsetY">
</line>
<text
v-if="timeSeries.length > 1"
class="legend-metric-title"
......
......@@ -9,6 +9,10 @@
type: String,
required: true,
},
lineStyle: {
type: String,
required: false,
},
lineColor: {
type: String,
required: true,
......@@ -18,6 +22,13 @@
required: true,
},
},
computed: {
strokeDashArray() {
if (this.lineStyle === 'dashed') return '3, 1';
if (this.lineStyle === 'dotted') return '1, 1';
return null;
},
},
};
</script>
<template>
......@@ -34,6 +45,7 @@
:stroke="lineColor"
fill="none"
stroke-width="1"
:stroke-dasharray="strokeDashArray"
transform="translate(-5, 20)">
</path>
</g>
......
......@@ -7,15 +7,16 @@ export default {
left: 40,
},
legends: {
width: 10,
width: 15,
height: 3,
offsetX: 20,
offsetY: 32,
},
backgroundLegend: {
width: 30,
height: 50,
},
axisLabelLineOffset: -20,
legendOffset: 33,
},
large: { // This covers both md and lg screen sizes
margin: {
......@@ -27,13 +28,14 @@ export default {
legends: {
width: 15,
height: 3,
offsetX: 20,
offsetY: 34,
},
backgroundLegend: {
width: 30,
height: 150,
},
axisLabelLineOffset: 20,
legendOffset: 36,
},
xTicks: 8,
yTicks: 3,
......
......@@ -11,7 +11,9 @@ const defaultColorPalette = {
const defaultColorOrder = ['blue', 'orange', 'red', 'green', 'purple'];
export default function createTimeSeries(queryData, graphWidth, graphHeight, graphHeightOffset) {
const defaultStyleOrder = ['solid', 'dashed', 'dotted'];
function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom, yDom, lineStyle) {
let usedColors = [];
function pickColor(name) {
......@@ -31,17 +33,7 @@ export default function createTimeSeries(queryData, graphWidth, graphHeight, gra
return defaultColorPalette[pick];
}
const maxValues = queryData.result.map((timeSeries, index) => {
const maxValue = d3.max(timeSeries.values.map(d => d.value));
return {
maxValue,
index,
};
});
const maxValueFromSeries = _.max(maxValues, val => val.maxValue);
return queryData.result.map((timeSeries, timeSeriesNumber) => {
return query.result.map((timeSeries, timeSeriesNumber) => {
let metricTag = '';
let lineColor = '';
let areaColor = '';
......@@ -52,9 +44,9 @@ export default function createTimeSeries(queryData, graphWidth, graphHeight, gra
const timeSeriesScaleY = d3.scale.linear()
.range([graphHeight - graphHeightOffset, 0]);
timeSeriesScaleX.domain(d3.extent(timeSeries.values, d => d.time));
timeSeriesScaleX.domain(xDom);
timeSeriesScaleX.ticks(d3.time.minute, 60);
timeSeriesScaleY.domain([0, maxValueFromSeries.maxValue]);
timeSeriesScaleY.domain(yDom);
const defined = d => !isNaN(d.value) && d.value != null;
......@@ -72,10 +64,10 @@ export default function createTimeSeries(queryData, graphWidth, graphHeight, gra
.y1(d => timeSeriesScaleY(d.value));
const timeSeriesMetricLabel = timeSeries.metric[Object.keys(timeSeries.metric)[0]];
const seriesCustomizationData = queryData.series != null &&
_.findWhere(queryData.series[0].when,
{ value: timeSeriesMetricLabel });
if (seriesCustomizationData != null) {
const seriesCustomizationData = query.series != null &&
_.findWhere(query.series[0].when, { value: timeSeriesMetricLabel });
if (seriesCustomizationData) {
metricTag = seriesCustomizationData.value || timeSeriesMetricLabel;
[lineColor, areaColor] = pickColor(seriesCustomizationData.color);
} else {
......@@ -83,14 +75,35 @@ export default function createTimeSeries(queryData, graphWidth, graphHeight, gra
[lineColor, areaColor] = pickColor();
}
if (query.track) {
metricTag += ` - ${query.track}`;
}
return {
linePath: lineFunction(timeSeries.values),
areaPath: areaFunction(timeSeries.values),
timeSeriesScaleX,
values: timeSeries.values,
lineStyle,
lineColor,
areaColor,
metricTag,
};
});
}
export default function createTimeSeries(queries, graphWidth, graphHeight, graphHeightOffset) {
const allValues = queries.reduce((allQueryResults, query) => allQueryResults.concat(
query.result.reduce((allResults, result) => allResults.concat(result.values), []),
), []);
const xDom = d3.extent(allValues, d => d.time);
const yDom = [0, d3.max(allValues.map(d => d.value))];
return queries.reduce((series, query, index) => {
const lineStyle = defaultStyleOrder[index % defaultStyleOrder.length];
return series.concat(
queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom, yDom, lineStyle),
);
}, []);
}
---
title: Allow multiple queries in a single Prometheus graph to support additional environments
(Canary, Staging, et al.)
merge_request: 15201
author:
type: added
......@@ -28,7 +28,7 @@ const defaultValuesComponent = {
currentDataIndex: 0,
};
const timeSeries = createTimeSeries(convertedMetrics[0].queries[0],
const timeSeries = createTimeSeries(convertedMetrics[0].queries,
defaultValuesComponent.graphWidth, defaultValuesComponent.graphHeight,
defaultValuesComponent.graphHeightOffset);
......
......@@ -13,7 +13,7 @@ const createComponent = (propsData) => {
const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeries);
const timeSeries = createTimeSeries(convertedMetrics[0].queries[0], 428, 272, 120);
const timeSeries = createTimeSeries(convertedMetrics[0].queries, 428, 272, 120);
const firstTimeSeries = timeSeries[0];
describe('Monitoring Paths', () => {
......
......@@ -2,7 +2,7 @@ import createTimeSeries from '~/monitoring/utils/multiple_time_series';
import { convertDatesMultipleSeries, singleRowMetricsMultipleSeries } from '../mock_data';
const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeries);
const timeSeries = createTimeSeries(convertedMetrics[0].queries[0], 428, 272, 120);
const timeSeries = createTimeSeries(convertedMetrics[0].queries, 428, 272, 120);
const firstTimeSeries = timeSeries[0];
describe('Multiple time series', () => {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment