Commit d3efad45 authored by Tim Zallmann's avatar Tim Zallmann

Merge branch 'support-additional-colors' into 'master'

Supports additional colors and labels for the prometheus monitoring dashboard

Closes #34629

See merge request !14065
parents bce1c509 4bd5c062
......@@ -13,7 +13,7 @@ export function formatRelevantDigits(number) {
let relevantDigits = 0;
let formattedNumber = '';
if (!isNaN(Number(number))) {
digitsLeft = number.split('.')[0];
digitsLeft = number.toString().split('.')[0];
switch (digitsLeft.length) {
case 1:
relevantDigits = 3;
......
......@@ -3,7 +3,7 @@
import GraphLegend from './graph/legend.vue';
import GraphFlag from './graph/flag.vue';
import GraphDeployment from './graph/deployment.vue';
import monitoringPaths from './monitoring_paths.vue';
import GraphPath from './graph_path.vue';
import MonitoringMixin from '../mixins/monitoring_mixins';
import eventHub from '../event_hub';
import measurements from '../utils/measurements';
......@@ -40,8 +40,6 @@
graphHeightOffset: 120,
margin: {},
unitOfDisplay: '',
areaColorRgb: '#8fbce8',
lineColorRgb: '#1f78d1',
yAxisLabel: '',
legendTitle: '',
reducedDeploymentData: [],
......@@ -63,7 +61,7 @@
GraphLegend,
GraphFlag,
GraphDeployment,
monitoringPaths,
GraphPath,
},
computed: {
......@@ -143,7 +141,7 @@
},
renderAxesPaths() {
this.timeSeries = createTimeSeries(this.graphData.queries[0].result,
this.timeSeries = createTimeSeries(this.graphData.queries[0],
this.graphWidth,
this.graphHeight,
this.graphHeightOffset);
......@@ -162,7 +160,7 @@
const xAxis = d3.svg.axis()
.scale(axisXScale)
.ticks(measurements.xTicks)
.ticks(d3.time.minute, 60)
.tickFormat(timeScaleFormat)
.orient('bottom');
......@@ -238,7 +236,7 @@
class="graph-data"
:viewBox="innerViewBox"
ref="graphData">
<monitoring-paths
<graph-path
v-for="(path, index) in timeSeries"
:key="index"
:generated-line-path="path.linePath"
......@@ -246,7 +244,7 @@
:line-color="path.lineColor"
:area-color="path.areaColor"
/>
<monitoring-deployment
<graph-deployment
:show-deploy-info="showDeployInfo"
:deployment-data="reducedDeploymentData"
:graph-height="graphHeight"
......
......@@ -81,6 +81,13 @@
formatMetricUsage(series) {
return `${formatRelevantDigits(series.values[this.currentDataIndex].value)} ${this.unitOfDisplay}`;
},
createSeriesString(index, series) {
if (series.metricTag) {
return `${series.metricTag} ${this.formatMetricUsage(series)}`;
}
return `${this.legendTitle} series ${index + 1} ${this.formatMetricUsage(series)}`;
},
},
mounted() {
this.$nextTick(() => {
......@@ -164,7 +171,7 @@
ref="legendTitleSvg"
x="38"
:y="graphHeight - 30">
{{legendTitle}} Series {{index + 1}} {{formatMetricUsage(series)}}
{{createSeriesString(index, series)}}
</text>
<text
v-else
......
import d3 from 'd3';
import _ from 'underscore';
export default function createTimeSeries(seriesData, graphWidth, graphHeight, graphHeightOffset) {
const maxValues = seriesData.map((timeSeries, index) => {
const defaultColorPalette = {
blue: ['#1f78d1', '#8fbce8'],
orange: ['#fc9403', '#feca81'],
red: ['#db3b21', '#ed9d90'],
green: ['#1aaa55', '#8dd5aa'],
purple: ['#6666c4', '#d1d1f0'],
};
const defaultColorOrder = ['blue', 'orange', 'red', 'green', 'purple'];
export default function createTimeSeries(queryData, graphWidth, graphHeight, graphHeightOffset) {
let usedColors = [];
function pickColor(name) {
let pick;
if (name && defaultColorPalette[name]) {
pick = name;
} else {
const unusedColors = _.difference(defaultColorOrder, usedColors);
if (unusedColors.length > 0) {
pick = unusedColors[0];
} else {
usedColors = [];
pick = defaultColorOrder[0];
}
}
usedColors.push(pick);
return defaultColorPalette[pick];
}
const maxValues = queryData.result.map((timeSeries, index) => {
const maxValue = d3.max(timeSeries.values.map(d => d.value));
return {
maxValue,
......@@ -12,10 +41,11 @@ export default function createTimeSeries(seriesData, graphWidth, graphHeight, gr
const maxValueFromSeries = _.max(maxValues, val => val.maxValue);
let timeSeriesNumber = 1;
let lineColor = '#1f78d1';
let areaColor = '#8fbce8';
return seriesData.map((timeSeries) => {
return queryData.result.map((timeSeries, timeSeriesNumber) => {
let metricTag = '';
let lineColor = '';
let areaColor = '';
const timeSeriesScaleX = d3.time.scale()
.range([0, graphWidth - 70]);
......@@ -23,49 +53,30 @@ export default function createTimeSeries(seriesData, graphWidth, graphHeight, gr
.range([graphHeight - graphHeightOffset, 0]);
timeSeriesScaleX.domain(d3.extent(timeSeries.values, d => d.time));
timeSeriesScaleX.ticks(d3.time.minute, 60);
timeSeriesScaleY.domain([0, maxValueFromSeries.maxValue]);
const lineFunction = d3.svg.line()
.interpolate('linear')
.x(d => timeSeriesScaleX(d.time))
.y(d => timeSeriesScaleY(d.value));
const areaFunction = d3.svg.area()
.interpolate('linear')
.x(d => timeSeriesScaleX(d.time))
.y0(graphHeight - graphHeightOffset)
.y1(d => timeSeriesScaleY(d.value))
.interpolate('linear');
switch (timeSeriesNumber) {
case 1:
lineColor = '#1f78d1';
areaColor = '#8fbce8';
break;
case 2:
lineColor = '#fc9403';
areaColor = '#feca81';
break;
case 3:
lineColor = '#db3b21';
areaColor = '#ed9d90';
break;
case 4:
lineColor = '#1aaa55';
areaColor = '#8dd5aa';
break;
case 5:
lineColor = '#6666c4';
areaColor = '#d1d1f0';
break;
default:
lineColor = '#1f78d1';
areaColor = '#8fbce8';
break;
}
.y1(d => timeSeriesScaleY(d.value));
if (timeSeriesNumber <= 5) {
timeSeriesNumber = timeSeriesNumber += 1;
const timeSeriesMetricLabel = timeSeries.metric[Object.keys(timeSeries.metric)[0]];
const seriesCustomizationData = queryData.series != null &&
_.findWhere(queryData.series[0].when,
{ value: timeSeriesMetricLabel });
if (seriesCustomizationData != null) {
metricTag = seriesCustomizationData.value || timeSeriesMetricLabel;
[lineColor, areaColor] = pickColor(seriesCustomizationData.color);
} else {
timeSeriesNumber = 1;
metricTag = timeSeriesMetricLabel || `series ${timeSeriesNumber + 1}`;
[lineColor, areaColor] = pickColor();
}
return {
......@@ -75,6 +86,7 @@ export default function createTimeSeries(seriesData, graphWidth, graphHeight, gr
values: timeSeries.values,
lineColor,
areaColor,
metricTag,
};
});
}
---
title: Added support for specific labels and colors
merge_request:
author:
type: changed
......@@ -28,7 +28,7 @@ const defaultValuesComponent = {
currentDataIndex: 0,
};
const timeSeries = createTimeSeries(convertedMetrics[0].queries[0].result,
const timeSeries = createTimeSeries(convertedMetrics[0].queries[0],
defaultValuesComponent.graphWidth, defaultValuesComponent.graphHeight,
defaultValuesComponent.graphHeightOffset);
......@@ -89,13 +89,12 @@ describe('GraphLegend', () => {
expect(component.$el.querySelectorAll('.rect-axis-text').length).toEqual(2);
});
it('contains text to signal the usage, title and time', () => {
it('contains text to signal the usage, title and time with multiple time series', () => {
const component = createComponent(defaultValuesComponent);
const titles = component.$el.querySelectorAll('.legend-metric-title');
expect(getTextFromNode(component, '.legend-metric-title').indexOf(component.legendTitle)).not.toEqual(-1);
expect(titles[0].textContent.indexOf('Title')).not.toEqual(-1);
expect(titles[1].textContent.indexOf('Series')).not.toEqual(-1);
expect(titles[0].textContent.indexOf('1xx')).not.toEqual(-1);
expect(titles[1].textContent.indexOf('2xx')).not.toEqual(-1);
expect(getTextFromNode(component, '.y-label-text')).toEqual(component.yAxisLabel);
});
......
import Vue from 'vue';
import MonitoringPaths from '~/monitoring/components/monitoring_paths.vue';
import GraphPath from '~/monitoring/components/graph_path.vue';
import createTimeSeries from '~/monitoring/utils/multiple_time_series';
import { singleRowMetricsMultipleSeries, convertDatesMultipleSeries } from './mock_data';
const createComponent = (propsData) => {
const Component = Vue.extend(MonitoringPaths);
const Component = Vue.extend(GraphPath);
return new Component({
propsData,
......@@ -13,22 +13,23 @@ const createComponent = (propsData) => {
const convertedMetrics = convertDatesMultipleSeries(singleRowMetricsMultipleSeries);
const timeSeries = createTimeSeries(convertedMetrics[0].queries[0].result, 428, 272, 120);
const timeSeries = createTimeSeries(convertedMetrics[0].queries[0], 428, 272, 120);
const firstTimeSeries = timeSeries[0];
describe('Monitoring Paths', () => {
it('renders two paths to represent a line and the area underneath it', () => {
const component = createComponent({
generatedLinePath: timeSeries[0].linePath,
generatedAreaPath: timeSeries[0].areaPath,
lineColor: '#ccc',
areaColor: '#fff',
generatedLinePath: firstTimeSeries.linePath,
generatedAreaPath: firstTimeSeries.areaPath,
lineColor: firstTimeSeries.lineColor,
areaColor: firstTimeSeries.areaColor,
});
const metricArea = component.$el.querySelector('.metric-area');
const metricLine = component.$el.querySelector('.metric-line');
expect(metricArea.getAttribute('fill')).toBe('#fff');
expect(metricArea.getAttribute('d')).toBe(timeSeries[0].areaPath);
expect(metricLine.getAttribute('stroke')).toBe('#ccc');
expect(metricLine.getAttribute('d')).toBe(timeSeries[0].linePath);
expect(metricArea.getAttribute('fill')).toBe('#8fbce8');
expect(metricArea.getAttribute('d')).toBe(firstTimeSeries.areaPath);
expect(metricLine.getAttribute('stroke')).toBe('#1f78d1');
expect(metricLine.getAttribute('d')).toBe(firstTimeSeries.linePath);
});
});
......@@ -6346,7 +6346,13 @@ export const singleRowMetricsMultipleSeries = [
}
]
},
]
],
'when': [
{
'value': 'hundred(s)',
'color': 'green',
},
],
}
]
},
......
......@@ -2,16 +2,17 @@ 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].result, 428, 272, 120);
const timeSeries = createTimeSeries(convertedMetrics[0].queries[0], 428, 272, 120);
const firstTimeSeries = timeSeries[0];
describe('Multiple time series', () => {
it('createTimeSeries returned array contains an object for each element', () => {
expect(typeof timeSeries[0].linePath).toEqual('string');
expect(typeof timeSeries[0].areaPath).toEqual('string');
expect(typeof timeSeries[0].timeSeriesScaleX).toEqual('function');
expect(typeof timeSeries[0].areaColor).toEqual('string');
expect(typeof timeSeries[0].lineColor).toEqual('string');
expect(timeSeries[0].values instanceof Array).toEqual(true);
expect(typeof firstTimeSeries.linePath).toEqual('string');
expect(typeof firstTimeSeries.areaPath).toEqual('string');
expect(typeof firstTimeSeries.timeSeriesScaleX).toEqual('function');
expect(typeof firstTimeSeries.areaColor).toEqual('string');
expect(typeof firstTimeSeries.lineColor).toEqual('string');
expect(firstTimeSeries.values instanceof Array).toEqual(true);
});
it('createTimeSeries returns an array', () => {
......
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