Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJose Ivan Vargas <jvargas@gitlab.com>2018-03-21 22:36:43 +0300
committerJose Ivan Vargas <jvargas@gitlab.com>2018-04-06 22:37:46 +0300
commit657fea868834ef239ab312381ac0956f3a84a07c (patch)
treec89112229b808ea79e74365fdbd67ae28cc27cfd /app/assets/javascripts/monitoring
parent9e3cdc02e62c3fc24cbd82615b9e7733136e34b7 (diff)
Add summary statistics prometheus dashboard
Diffstat (limited to 'app/assets/javascripts/monitoring')
-rw-r--r--app/assets/javascripts/monitoring/components/graph.vue32
-rw-r--r--app/assets/javascripts/monitoring/components/graph/axis.vue127
-rw-r--r--app/assets/javascripts/monitoring/components/graph/flag.vue2
-rw-r--r--app/assets/javascripts/monitoring/components/graph/legend.vue203
-rw-r--r--app/assets/javascripts/monitoring/utils/multiple_time_series.js82
5 files changed, 255 insertions, 191 deletions
diff --git a/app/assets/javascripts/monitoring/components/graph.vue b/app/assets/javascripts/monitoring/components/graph.vue
index 04d546fafa0..9bf32d8a2da 100644
--- a/app/assets/javascripts/monitoring/components/graph.vue
+++ b/app/assets/javascripts/monitoring/components/graph.vue
@@ -3,6 +3,7 @@ import { scaleLinear, scaleTime } from 'd3-scale';
import { axisLeft, axisBottom } from 'd3-axis';
import { max, extent } from 'd3-array';
import { select } from 'd3-selection';
+import GraphAxis from './graph/axis.vue';
import GraphLegend from './graph/legend.vue';
import GraphFlag from './graph/flag.vue';
import GraphDeployment from './graph/deployment.vue';
@@ -18,10 +19,11 @@ const d3 = { scaleLinear, scaleTime, axisLeft, axisBottom, max, extent, select }
export default {
components: {
- GraphLegend,
+ GraphAxis,
GraphFlag,
GraphDeployment,
GraphPath,
+ GraphLegend,
},
mixins: [MonitoringMixin],
props: {
@@ -138,7 +140,7 @@ export default {
this.legendTitle = query.label || 'Average';
this.graphWidth = this.$refs.baseSvg.clientWidth - this.margin.left - this.margin.right;
this.graphHeight = this.graphHeight - this.margin.top - this.margin.bottom;
- this.baseGraphHeight = this.graphHeight;
+ this.baseGraphHeight = this.graphHeight - 50;
this.baseGraphWidth = this.graphWidth;
// pixel offsets inside the svg and outside are not 1:1
@@ -177,14 +179,10 @@ export default {
this.graphHeightOffset,
);
- if (!this.showLegend) {
- this.baseGraphHeight -= 50;
- } else if (this.timeSeries.length > 3) {
- this.baseGraphHeight = this.baseGraphHeight += (this.timeSeries.length - 3) * 20;
- }
-
- const axisXScale = d3.scaleTime().range([0, this.graphWidth - 70]);
- const axisYScale = d3.scaleLinear().range([this.graphHeight - this.graphHeightOffset, 0]);
+ const axisXScale = d3.scaleTime()
+ .range([0, this.graphWidth - 70]);
+ const axisYScale = d3.scaleLinear()
+ .range([this.graphHeight - this.graphHeightOffset, 0]);
const allValues = this.timeSeries.reduce((all, { values }) => all.concat(values), []);
axisXScale.domain(d3.extent(allValues, d => d.time));
@@ -251,17 +249,12 @@ export default {
class="y-axis"
transform="translate(70, 20)"
/>
- <graph-legend
+ <graph-axis
:graph-width="graphWidth"
:graph-height="graphHeight"
:margin="margin"
:measurements="measurements"
- :legend-title="legendTitle"
:y-axis-label="yAxisLabel"
- :time-series="timeSeries"
- :unit-of-display="unitOfDisplay"
- :current-data-index="currentDataIndex"
- :show-legend-group="showLegend"
/>
<svg
class="graph-data"
@@ -306,5 +299,12 @@ export default {
:deployment-flag-data="deploymentFlagData"
/>
</div>
+ <graph-legend
+ v-if="showLegend"
+ :legend-title="legendTitle"
+ :time-series="timeSeries"
+ :current-data-index="currentDataIndex"
+ :unit-of-display="unitOfDisplay"
+ />
</div>
</template>
diff --git a/app/assets/javascripts/monitoring/components/graph/axis.vue b/app/assets/javascripts/monitoring/components/graph/axis.vue
new file mode 100644
index 00000000000..ae397fc39e2
--- /dev/null
+++ b/app/assets/javascripts/monitoring/components/graph/axis.vue
@@ -0,0 +1,127 @@
+<script>
+export default {
+ props: {
+ graphWidth: {
+ type: Number,
+ required: true,
+ },
+ graphHeight: {
+ type: Number,
+ required: true,
+ },
+ margin: {
+ type: Object,
+ required: true,
+ },
+ measurements: {
+ type: Object,
+ required: true,
+ },
+ yAxisLabel: {
+ type: String,
+ required: true,
+ },
+ },
+ data() {
+ return {
+ yLabelWidth: 0,
+ yLabelHeight: 0,
+ };
+ },
+ computed: {
+ textTransform() {
+ const yCoordinate =
+ (this.graphHeight -
+ this.margin.top +
+ this.measurements.axisLabelLineOffset) /
+ 2 || 0;
+
+ return `translate(15, ${yCoordinate}) rotate(-90)`;
+ },
+
+ rectTransform() {
+ const yCoordinate =
+ (this.graphHeight -
+ this.margin.top +
+ this.measurements.axisLabelLineOffset) /
+ 2 +
+ this.yLabelWidth / 2 || 0;
+
+ return `translate(0, ${yCoordinate}) rotate(-90)`;
+ },
+
+ xPosition() {
+ return (
+ (this.graphWidth + this.measurements.axisLabelLineOffset) / 2 -
+ this.margin.right || 0
+ );
+ },
+
+ yPosition() {
+ return (
+ this.graphHeight -
+ this.margin.top +
+ this.measurements.axisLabelLineOffset || 0
+ );
+ },
+ },
+ mounted() {
+ this.$nextTick(() => {
+ const bbox = this.$refs.ylabel.getBBox();
+ this.yLabelWidth = bbox.width + 10; // Added some padding
+ this.yLabelHeight = bbox.height + 5;
+ });
+ },
+};
+</script>
+<template>
+ <g class="axis-label-container">
+ <line
+ class="label-x-axis-line"
+ stroke="#000000"
+ stroke-width="1"
+ x1="10"
+ :y1="yPosition"
+ :x2="graphWidth + 20"
+ :y2="yPosition"
+ />
+ <line
+ class="label-y-axis-line"
+ stroke="#000000"
+ stroke-width="1"
+ x1="10"
+ y1="0"
+ :x2="10"
+ :y2="yPosition"
+ />
+ <rect
+ class="rect-axis-text"
+ :transform="rectTransform"
+ :width="yLabelWidth"
+ :height="yLabelHeight"
+ />
+ <text
+ class="label-axis-text y-label-text"
+ text-anchor="middle"
+ :transform="textTransform"
+ ref="ylabel"
+ >
+ {{ yAxisLabel }}
+ </text>
+ <rect
+ class="rect-axis-text"
+ :x="xPosition + 60"
+ :y="graphHeight - 80"
+ width="35"
+ height="50"
+ />
+ <text
+ class="label-axis-text x-label-text"
+ :x="xPosition + 60"
+ :y="yPosition"
+ dy=".35em"
+ >
+ Time
+ </text>
+ </g>
+</template>
diff --git a/app/assets/javascripts/monitoring/components/graph/flag.vue b/app/assets/javascripts/monitoring/components/graph/flag.vue
index 906c7c51f52..8e6a830f08c 100644
--- a/app/assets/javascripts/monitoring/components/graph/flag.vue
+++ b/app/assets/javascripts/monitoring/components/graph/flag.vue
@@ -160,7 +160,7 @@ export default {
</div>
</div>
<div class="popover-content">
- <table>
+ <table class="prometheus-table">
<tr
v-for="(series, index) in timeSeries"
:key="index"
diff --git a/app/assets/javascripts/monitoring/components/graph/legend.vue b/app/assets/javascripts/monitoring/components/graph/legend.vue
index a7a058a9203..ac1c6e34cc4 100644
--- a/app/assets/javascripts/monitoring/components/graph/legend.vue
+++ b/app/assets/javascripts/monitoring/components/graph/legend.vue
@@ -1,112 +1,50 @@
<script>
-import { formatRelevantDigits } from '../../../lib/utils/number_utils';
+import { formatRelevantDigits } from '~/lib/utils/number_utils';
export default {
props: {
- graphWidth: {
- type: Number,
- required: true,
- },
- graphHeight: {
- type: Number,
- required: true,
- },
- margin: {
- type: Object,
- required: true,
- },
- measurements: {
- type: Object,
- required: true,
- },
legendTitle: {
type: String,
required: true,
},
- yAxisLabel: {
- type: String,
- required: true,
- },
timeSeries: {
type: Array,
required: true,
},
- unitOfDisplay: {
- type: String,
- required: true,
- },
currentDataIndex: {
type: Number,
required: true,
},
- showLegendGroup: {
- type: Boolean,
- required: false,
- default: true,
- },
- },
- data() {
- return {
- yLabelWidth: 0,
- yLabelHeight: 0,
- seriesXPosition: 0,
- metricUsageXPosition: 0,
- };
- },
- computed: {
- textTransform() {
- const yCoordinate =
- (this.graphHeight - this.margin.top + this.measurements.axisLabelLineOffset) / 2 || 0;
-
- return `translate(15, ${yCoordinate}) rotate(-90)`;
- },
- rectTransform() {
- const yCoordinate =
- (this.graphHeight - this.margin.top + this.measurements.axisLabelLineOffset) / 2 +
- this.yLabelWidth / 2 || 0;
-
- return `translate(0, ${yCoordinate}) rotate(-90)`;
- },
- xPosition() {
- return (this.graphWidth + this.measurements.axisLabelLineOffset) / 2 - this.margin.right || 0;
- },
- yPosition() {
- return this.graphHeight - this.margin.top + this.measurements.axisLabelLineOffset || 0;
+ unitOfDisplay: {
+ type: String,
+ required: true,
},
},
- mounted() {
- this.$nextTick(() => {
- const bbox = this.$refs.ylabel.getBBox();
- this.metricUsageXPosition = 0;
- this.seriesXPosition = 0;
- if (this.$refs.legendTitleSvg != null) {
- this.seriesXPosition = this.$refs.legendTitleSvg[0].getBBox().width;
- }
- if (this.$refs.seriesTitleSvg != null) {
- this.metricUsageXPosition = this.$refs.seriesTitleSvg[0].getBBox().width;
- }
- this.yLabelWidth = bbox.width + 10; // Added some padding
- this.yLabelHeight = bbox.height + 5;
- });
- },
methods: {
- translateLegendGroup(index) {
- return `translate(0, ${12 * index})`;
- },
formatMetricUsage(series) {
const value =
- series.values[this.currentDataIndex] && series.values[this.currentDataIndex].value;
+ series.values[this.currentDataIndex] &&
+ series.values[this.currentDataIndex].value;
if (isNaN(value)) {
return '-';
}
return `${formatRelevantDigits(value)} ${this.unitOfDisplay}`;
},
+
createSeriesString(index, series) {
if (series.metricTag) {
return `${series.metricTag} ${this.formatMetricUsage(series)}`;
}
- return `${this.legendTitle} series ${index + 1} ${this.formatMetricUsage(series)}`;
+ return `${this.legendTitle} series ${index + 1} ${this.formatMetricUsage(
+ series,
+ )}`;
+ },
+
+ summaryMetrics(series) {
+ return `Avg: ${formatRelevantDigits(series.average)} ${this.unitOfDisplay},
+ Max: ${formatRelevantDigits(series.max)} ${this.unitOfDisplay}`;
},
+
strokeDashArray(type) {
if (type === 'dashed') return '6, 3';
if (type === 'dotted') return '3, 3';
@@ -116,89 +54,38 @@ export default {
};
</script>
<template>
- <g class="axis-label-container">
- <line
- class="label-x-axis-line"
- stroke="#000000"
- stroke-width="1"
- x1="10"
- :y1="yPosition"
- :x2="graphWidth + 20"
- :y2="yPosition"
- />
- <line
- class="label-y-axis-line"
- stroke="#000000"
- stroke-width="1"
- x1="10"
- y1="0"
- :x2="10"
- :y2="yPosition"
- />
- <rect
- class="rect-axis-text"
- :transform="rectTransform"
- :width="yLabelWidth"
- :height="yLabelHeight"
- />
- <text
- class="label-axis-text y-label-text"
- text-anchor="middle"
- :transform="textTransform"
- ref="ylabel"
- >
- {{ yAxisLabel }}
- </text>
- <rect
- class="rect-axis-text"
- :x="xPosition + 60"
- :y="graphHeight - 80"
- width="35"
- height="50"
- />
- <text
- class="label-axis-text x-label-text"
- :x="xPosition + 60"
- :y="yPosition"
- dy=".35em"
- >
- Time
- </text>
- <template v-if="showLegendGroup">
- <g
- class="legend-group"
+ <div class="prometheus-graph-legends prepend-left-10">
+ <table class="prometheus-table">
+ <tr
v-for="(series, index) in timeSeries"
:key="index"
- :transform="translateLegendGroup(index)"
>
- <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"
- />
- <text
- v-if="timeSeries.length > 1"
+ <td>
+ <svg
+ width="15"
+ height="6"
+ >
+ <line
+ :stroke-dasharray="strokeDashArray(series.lineStyle)"
+ :stroke="series.lineColor"
+ stroke-width="4"
+ :x1="0"
+ :x2="15"
+ :y1="2"
+ :y2="2"
+ />
+ </svg>
+ </td>
+ <td
class="legend-metric-title"
- ref="legendTitleSvg"
- x="38"
- :y="graphHeight - 30"
- >
- {{ createSeriesString(index, series) }}
- </text>
- <text
- v-else
- class="legend-metric-title"
- ref="legendTitleSvg"
- x="38"
- :y="graphHeight - 30"
+ v-if="timeSeries.length > 1"
>
- {{ legendTitle }} {{ formatMetricUsage(series) }}
- </text>
- </g>
- </template>
- </g>
+ {{ createSeriesString(index, series) }}, {{ summaryMetrics(series) }}
+ </td>
+ <td v-else>
+ {{ legendTitle }} {{ formatMetricUsage(series) }}, {{ summaryMetrics(series) }}
+ </td>
+ </tr>
+ </table>
+ </div>
</template>
diff --git a/app/assets/javascripts/monitoring/utils/multiple_time_series.js b/app/assets/javascripts/monitoring/utils/multiple_time_series.js
index b5b8e3c255d..95db31c7a2a 100644
--- a/app/assets/javascripts/monitoring/utils/multiple_time_series.js
+++ b/app/assets/javascripts/monitoring/utils/multiple_time_series.js
@@ -1,10 +1,20 @@
import _ from 'underscore';
import { scaleLinear, scaleTime } from 'd3-scale';
import { line, area, curveLinear } from 'd3-shape';
-import { extent, max } from 'd3-array';
+import { extent, max, sum } from 'd3-array';
import { timeMinute } from 'd3-time';
-const d3 = { scaleLinear, scaleTime, line, area, curveLinear, extent, max, timeMinute };
+const d3 = {
+ scaleLinear,
+ scaleTime,
+ line,
+ area,
+ curveLinear,
+ extent,
+ max,
+ timeMinute,
+ sum,
+};
const defaultColorPalette = {
blue: ['#1f78d1', '#8fbce8'],
@@ -18,7 +28,15 @@ const defaultColorOrder = ['blue', 'orange', 'red', 'green', 'purple'];
const defaultStyleOrder = ['solid', 'dashed', 'dotted'];
-function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom, yDom, lineStyle) {
+function queryTimeSeries(
+ query,
+ graphWidth,
+ graphHeight,
+ graphHeightOffset,
+ xDom,
+ yDom,
+ lineStyle,
+) {
let usedColors = [];
function pickColor(name) {
@@ -42,11 +60,14 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom
let metricTag = '';
let lineColor = '';
let areaColor = '';
+ const timeSeriesValues = timeSeries.values.map(d => d.value);
+ const maximumValue = d3.max(timeSeriesValues);
+ const accum = d3.sum(timeSeriesValues);
- const timeSeriesScaleX = d3.scaleTime()
- .range([0, graphWidth - 70]);
+ const timeSeriesScaleX = d3.scaleTime().range([0, graphWidth - 70]);
- const timeSeriesScaleY = d3.scaleLinear()
+ const timeSeriesScaleY = d3
+ .scaleLinear()
.range([graphHeight - graphHeightOffset, 0]);
timeSeriesScaleX.domain(xDom);
@@ -55,28 +76,35 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom
const defined = d => !isNaN(d.value) && d.value != null;
- const lineFunction = d3.line()
+ const lineFunction = d3
+ .line()
.defined(defined)
.curve(d3.curveLinear) // d3 v4 uses curbe instead of interpolate
.x(d => timeSeriesScaleX(d.time))
.y(d => timeSeriesScaleY(d.value));
- const areaFunction = d3.area()
+ const areaFunction = d3
+ .area()
.defined(defined)
.curve(d3.curveLinear)
.x(d => timeSeriesScaleX(d.time))
.y0(graphHeight - graphHeightOffset)
.y1(d => timeSeriesScaleY(d.value));
- const timeSeriesMetricLabel = timeSeries.metric[Object.keys(timeSeries.metric)[0]];
- const seriesCustomizationData = query.series != null &&
+ const timeSeriesMetricLabel =
+ timeSeries.metric[Object.keys(timeSeries.metric)[0]];
+ const seriesCustomizationData =
+ query.series != null &&
_.findWhere(query.series[0].when, { value: timeSeriesMetricLabel });
if (seriesCustomizationData) {
metricTag = seriesCustomizationData.value || timeSeriesMetricLabel;
[lineColor, areaColor] = pickColor(seriesCustomizationData.color);
} else {
- metricTag = timeSeriesMetricLabel || query.label || `series ${timeSeriesNumber + 1}`;
+ metricTag =
+ timeSeriesMetricLabel ||
+ query.label ||
+ `series ${timeSeriesNumber + 1}`;
[lineColor, areaColor] = pickColor();
}
@@ -89,6 +117,8 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom
areaPath: areaFunction(timeSeries.values),
timeSeriesScaleX,
values: timeSeries.values,
+ max: maximumValue,
+ average: accum / timeSeries.values.length,
lineStyle,
lineColor,
areaColor,
@@ -97,10 +127,22 @@ function queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom
});
}
-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), []),
- ), []);
+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))];
@@ -108,7 +150,15 @@ export default function createTimeSeries(queries, graphWidth, graphHeight, graph
return queries.reduce((series, query, index) => {
const lineStyle = defaultStyleOrder[index % defaultStyleOrder.length];
return series.concat(
- queryTimeSeries(query, graphWidth, graphHeight, graphHeightOffset, xDom, yDom, lineStyle),
+ queryTimeSeries(
+ query,
+ graphWidth,
+ graphHeight,
+ graphHeightOffset,
+ xDom,
+ yDom,
+ lineStyle,
+ ),
);
}, []);
}