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
...@@ -138,7 +138,7 @@ ...@@ -138,7 +138,7 @@
renderAxesPaths() { renderAxesPaths() {
this.timeSeries = createTimeSeries( this.timeSeries = createTimeSeries(
this.graphData.queries[0], this.graphData.queries,
this.graphWidth, this.graphWidth,
this.graphHeight, this.graphHeight,
this.graphHeightOffset, this.graphHeightOffset,
...@@ -153,8 +153,9 @@ ...@@ -153,8 +153,9 @@
const axisYScale = d3.scale.linear() const axisYScale = d3.scale.linear()
.range([this.graphHeight - this.graphHeightOffset, 0]); .range([this.graphHeight - this.graphHeightOffset, 0]);
axisXScale.domain(d3.extent(this.timeSeries[0].values, d => d.time)); const allValues = this.timeSeries.reduce((all, { values }) => all.concat(values), []);
axisYScale.domain([0, d3.max(this.timeSeries[0].values.map(d => d.value))]); axisXScale.domain(d3.extent(allValues, d => d.time));
axisYScale.domain([0, d3.max(allValues.map(d => d.value))]);
const xAxis = d3.svg.axis() const xAxis = d3.svg.axis()
.scale(axisXScale) .scale(axisXScale)
...@@ -246,6 +247,7 @@ ...@@ -246,6 +247,7 @@
:key="index" :key="index"
:generated-line-path="path.linePath" :generated-line-path="path.linePath"
:generated-area-path="path.areaPath" :generated-area-path="path.areaPath"
:line-style="path.lineStyle"
:line-color="path.lineColor" :line-color="path.lineColor"
:area-color="path.areaColor" :area-color="path.areaColor"
/> />
......
...@@ -79,7 +79,8 @@ ...@@ -79,7 +79,8 @@
}, },
formatMetricUsage(series) { formatMetricUsage(series) {
const value = series.values[this.currentDataIndex].value; const value = series.values[this.currentDataIndex] &&
series.values[this.currentDataIndex].value;
if (isNaN(value)) { if (isNaN(value)) {
return '-'; return '-';
} }
...@@ -92,6 +93,12 @@ ...@@ -92,6 +93,12 @@
} }
return `${this.legendTitle} series ${index + 1} ${this.formatMetricUsage(series)}`; 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() { mounted() {
this.$nextTick(() => { this.$nextTick(() => {
...@@ -162,13 +169,15 @@ ...@@ -162,13 +169,15 @@
v-for="(series, index) in timeSeries" v-for="(series, index) in timeSeries"
:key="index" :key="index"
:transform="translateLegendGroup(index)"> :transform="translateLegendGroup(index)">
<rect <line
:fill="series.areaColor" :stroke="series.lineColor"
:width="measurements.legends.width" :stroke-width="measurements.legends.height"
:height="measurements.legends.height" :stroke-dasharray="strokeDashArray(series.lineStyle)"
x="20" :x1="measurements.legends.offsetX"
:y="graphHeight - measurements.legendOffset"> :x2="measurements.legends.offsetX + measurements.legends.width"
</rect> :y1="graphHeight - measurements.legends.offsetY"
:y2="graphHeight - measurements.legends.offsetY">
</line>
<text <text
v-if="timeSeries.length > 1" v-if="timeSeries.length > 1"
class="legend-metric-title" class="legend-metric-title"
......
...@@ -9,6 +9,10 @@ ...@@ -9,6 +9,10 @@
type: String, type: String,
required: true, required: true,
}, },
lineStyle: {
type: String,
required: false,
},
lineColor: { lineColor: {
type: String, type: String,
required: true, required: true,
...@@ -18,6 +22,13 @@ ...@@ -18,6 +22,13 @@
required: true, required: true,
}, },
}, },
computed: {
strokeDashArray() {
if (this.lineStyle === 'dashed') return '3, 1';
if (this.lineStyle === 'dotted') return '1, 1';
return null;
},
},
}; };
</script> </script>
<template> <template>
...@@ -34,6 +45,7 @@ ...@@ -34,6 +45,7 @@
:stroke="lineColor" :stroke="lineColor"
fill="none" fill="none"
stroke-width="1" stroke-width="1"
:stroke-dasharray="strokeDashArray"
transform="translate(-5, 20)"> transform="translate(-5, 20)">
</path> </path>
</g> </g>
......
...@@ -7,15 +7,16 @@ export default { ...@@ -7,15 +7,16 @@ export default {
left: 40, left: 40,
}, },
legends: { legends: {
width: 10, width: 15,
height: 3, height: 3,
offsetX: 20,
offsetY: 32,
}, },
backgroundLegend: { backgroundLegend: {
width: 30, width: 30,
height: 50, height: 50,
}, },
axisLabelLineOffset: -20, axisLabelLineOffset: -20,
legendOffset: 33,
}, },
large: { // This covers both md and lg screen sizes large: { // This covers both md and lg screen sizes
margin: { margin: {
...@@ -27,13 +28,14 @@ export default { ...@@ -27,13 +28,14 @@ export default {
legends: { legends: {
width: 15, width: 15,
height: 3, height: 3,
offsetX: 20,
offsetY: 34,
}, },
backgroundLegend: { backgroundLegend: {
width: 30, width: 30,
height: 150, height: 150,
}, },
axisLabelLineOffset: 20, axisLabelLineOffset: 20,
legendOffset: 36,
}, },
xTicks: 8, xTicks: 8,
yTicks: 3, yTicks: 3,
......
...@@ -11,7 +11,9 @@ const defaultColorPalette = { ...@@ -11,7 +11,9 @@ const defaultColorPalette = {
const defaultColorOrder = ['blue', 'orange', 'red', 'green', 'purple']; 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 = []; let usedColors = [];
function pickColor(name) { function pickColor(name) {
...@@ -31,17 +33,7 @@ export default function createTimeSeries(queryData, graphWidth, graphHeight, gra ...@@ -31,17 +33,7 @@ export default function createTimeSeries(queryData, graphWidth, graphHeight, gra
return defaultColorPalette[pick]; return defaultColorPalette[pick];
} }
const maxValues = queryData.result.map((timeSeries, index) => { return query.result.map((timeSeries, timeSeriesNumber) => {
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) => {
let metricTag = ''; let metricTag = '';
let lineColor = ''; let lineColor = '';
let areaColor = ''; let areaColor = '';
...@@ -52,9 +44,9 @@ export default function createTimeSeries(queryData, graphWidth, graphHeight, gra ...@@ -52,9 +44,9 @@ export default function createTimeSeries(queryData, graphWidth, graphHeight, gra
const timeSeriesScaleY = d3.scale.linear() const timeSeriesScaleY = d3.scale.linear()
.range([graphHeight - graphHeightOffset, 0]); .range([graphHeight - graphHeightOffset, 0]);
timeSeriesScaleX.domain(d3.extent(timeSeries.values, d => d.time)); timeSeriesScaleX.domain(xDom);
timeSeriesScaleX.ticks(d3.time.minute, 60); timeSeriesScaleX.ticks(d3.time.minute, 60);
timeSeriesScaleY.domain([0, maxValueFromSeries.maxValue]); timeSeriesScaleY.domain(yDom);
const defined = d => !isNaN(d.value) && d.value != null; const defined = d => !isNaN(d.value) && d.value != null;
...@@ -72,10 +64,10 @@ export default function createTimeSeries(queryData, graphWidth, graphHeight, gra ...@@ -72,10 +64,10 @@ export default function createTimeSeries(queryData, graphWidth, graphHeight, gra
.y1(d => timeSeriesScaleY(d.value)); .y1(d => timeSeriesScaleY(d.value));
const timeSeriesMetricLabel = timeSeries.metric[Object.keys(timeSeries.metric)[0]]; const timeSeriesMetricLabel = timeSeries.metric[Object.keys(timeSeries.metric)[0]];
const seriesCustomizationData = queryData.series != null && const seriesCustomizationData = query.series != null &&
_.findWhere(queryData.series[0].when, _.findWhere(query.series[0].when, { value: timeSeriesMetricLabel });
{ value: timeSeriesMetricLabel });
if (seriesCustomizationData != null) { if (seriesCustomizationData) {
metricTag = seriesCustomizationData.value || timeSeriesMetricLabel; metricTag = seriesCustomizationData.value || timeSeriesMetricLabel;
[lineColor, areaColor] = pickColor(seriesCustomizationData.color); [lineColor, areaColor] = pickColor(seriesCustomizationData.color);
} else { } else {
...@@ -83,14 +75,35 @@ export default function createTimeSeries(queryData, graphWidth, graphHeight, gra ...@@ -83,14 +75,35 @@ export default function createTimeSeries(queryData, graphWidth, graphHeight, gra
[lineColor, areaColor] = pickColor(); [lineColor, areaColor] = pickColor();
} }
if (query.track) {
metricTag += ` - ${query.track}`;
}
return { return {
linePath: lineFunction(timeSeries.values), linePath: lineFunction(timeSeries.values),
areaPath: areaFunction(timeSeries.values), areaPath: areaFunction(timeSeries.values),
timeSeriesScaleX, timeSeriesScaleX,
values: timeSeries.values, values: timeSeries.values,
lineStyle,
lineColor, lineColor,
areaColor, areaColor,
metricTag, 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 = { ...@@ -28,7 +28,7 @@ const defaultValuesComponent = {
currentDataIndex: 0, currentDataIndex: 0,
}; };
const timeSeries = createTimeSeries(convertedMetrics[0].queries[0], const timeSeries = createTimeSeries(convertedMetrics[0].queries,
defaultValuesComponent.graphWidth, defaultValuesComponent.graphHeight, defaultValuesComponent.graphWidth, defaultValuesComponent.graphHeight,
defaultValuesComponent.graphHeightOffset); defaultValuesComponent.graphHeightOffset);
......
...@@ -13,7 +13,7 @@ const createComponent = (propsData) => { ...@@ -13,7 +13,7 @@ const createComponent = (propsData) => {
const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeries); 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]; const firstTimeSeries = timeSeries[0];
describe('Monitoring Paths', () => { describe('Monitoring Paths', () => {
......
...@@ -2,7 +2,7 @@ import createTimeSeries from '~/monitoring/utils/multiple_time_series'; ...@@ -2,7 +2,7 @@ import createTimeSeries from '~/monitoring/utils/multiple_time_series';
import { convertDatesMultipleSeries, singleRowMetricsMultipleSeries } from '../mock_data'; import { convertDatesMultipleSeries, singleRowMetricsMultipleSeries } from '../mock_data';
const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeries); 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]; const firstTimeSeries = timeSeries[0];
describe('Multiple time series', () => { 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