diff options
Diffstat (limited to 'app/assets/javascripts/monitoring/prometheus_graph.js')
-rw-r--r-- | app/assets/javascripts/monitoring/prometheus_graph.js | 433 |
1 files changed, 0 insertions, 433 deletions
diff --git a/app/assets/javascripts/monitoring/prometheus_graph.js b/app/assets/javascripts/monitoring/prometheus_graph.js deleted file mode 100644 index 6af88769129..00000000000 --- a/app/assets/javascripts/monitoring/prometheus_graph.js +++ /dev/null @@ -1,433 +0,0 @@ -/* eslint-disable no-new */ -/* global Flash */ - -import d3 from 'd3'; -import statusCodes from '~/lib/utils/http_status'; -import Deployments from './deployments'; -import '../lib/utils/common_utils'; -import { formatRelevantDigits } from '../lib/utils/number_utils'; -import '../flash'; -import { - dateFormat, - timeFormat, -} from './constants'; - -const prometheusContainer = '.prometheus-container'; -const prometheusParentGraphContainer = '.prometheus-graphs'; -const prometheusGraphsContainer = '.prometheus-graph'; -const prometheusStatesContainer = '.prometheus-state'; -const metricsEndpoint = 'metrics.json'; -const bisectDate = d3.bisector(d => d.time).left; -const extraAddedWidthParent = 100; - -class PrometheusGraph { - constructor() { - const $prometheusContainer = $(prometheusContainer); - const hasMetrics = $prometheusContainer.data('has-metrics'); - this.docLink = $prometheusContainer.data('doc-link'); - this.integrationLink = $prometheusContainer.data('prometheus-integration'); - this.state = ''; - - $(document).ajaxError(() => {}); - - if (hasMetrics) { - this.margin = { top: 80, right: 180, bottom: 80, left: 100 }; - this.marginLabelContainer = { top: 40, right: 0, bottom: 40, left: 0 }; - const parentContainerWidth = $(prometheusGraphsContainer).parent().width() + - extraAddedWidthParent; - this.originalWidth = parentContainerWidth; - this.originalHeight = 330; - this.width = parentContainerWidth - this.margin.left - this.margin.right; - this.height = this.originalHeight - this.margin.top - this.margin.bottom; - this.backOffRequestCounter = 0; - this.deployments = new Deployments(this.width, this.height); - this.configureGraph(); - this.init(); - } else { - const prevState = this.state; - this.state = '.js-getting-started'; - this.updateState(prevState); - } - } - - createGraph() { - Object.keys(this.graphSpecificProperties).forEach((key) => { - const value = this.graphSpecificProperties[key]; - if (value.data.length > 0) { - this.plotValues(key); - } - }); - } - - init() { - return this.getData().then((metricsResponse) => { - let enoughData = true; - if (typeof metricsResponse === 'undefined') { - enoughData = false; - } else { - Object.keys(metricsResponse.metrics).forEach((key) => { - if (key === 'cpu_values' || key === 'memory_values') { - const currentData = (metricsResponse.metrics[key])[0]; - if (currentData.values.length <= 2) { - enoughData = false; - } - } - }); - } - if (enoughData) { - $(prometheusStatesContainer).hide(); - $(prometheusParentGraphContainer).show(); - this.transformData(metricsResponse); - this.createGraph(); - - const firstMetricData = this.graphSpecificProperties[ - Object.keys(this.graphSpecificProperties)[0] - ].data; - - this.deployments.init(firstMetricData); - } - }); - } - - plotValues(key) { - const graphSpecifics = this.graphSpecificProperties[key]; - - const x = d3.time.scale() - .range([0, this.width]); - - const y = d3.scale.linear() - .range([this.height, 0]); - - graphSpecifics.xScale = x; - graphSpecifics.yScale = y; - - const prometheusGraphContainer = `${prometheusGraphsContainer}[graph-type=${key}]`; - - const chart = d3.select(prometheusGraphContainer) - .attr('width', this.width + this.margin.left + this.margin.right) - .attr('height', this.height + this.margin.bottom + this.margin.top) - .append('g') - .attr('class', 'graph-container') - .attr('transform', `translate(${this.margin.left},${this.margin.top})`); - - const axisLabelContainer = d3.select(prometheusGraphContainer) - .attr('width', this.originalWidth) - .attr('height', this.originalHeight) - .append('g') - .attr('transform', `translate(${this.marginLabelContainer.left},${this.marginLabelContainer.top})`); - - x.domain(d3.extent(graphSpecifics.data, d => d.time)); - y.domain([0, d3.max(graphSpecifics.data.map(metricValue => metricValue.value))]); - - const xAxis = d3.svg.axis() - .scale(x) - .ticks(this.commonGraphProperties.axis_no_ticks) - .orient('bottom'); - - const yAxis = d3.svg.axis() - .scale(y) - .ticks(this.commonGraphProperties.axis_no_ticks) - .tickSize(-this.width) - .outerTickSize(0) - .orient('left'); - - this.createAxisLabelContainers(axisLabelContainer, key); - - chart.append('g') - .attr('class', 'x-axis') - .attr('transform', `translate(0,${this.height})`) - .call(xAxis); - - chart.append('g') - .attr('class', 'y-axis') - .call(yAxis); - - const area = d3.svg.area() - .x(d => x(d.time)) - .y0(this.height) - .y1(d => y(d.value)) - .interpolate('linear'); - - const line = d3.svg.line() - .x(d => x(d.time)) - .y(d => y(d.value)); - - chart.append('path') - .datum(graphSpecifics.data) - .attr('d', area) - .attr('class', 'metric-area') - .attr('fill', graphSpecifics.area_fill_color); - - chart.append('path') - .datum(graphSpecifics.data) - .attr('class', 'metric-line') - .attr('stroke', graphSpecifics.line_color) - .attr('fill', 'none') - .attr('stroke-width', this.commonGraphProperties.area_stroke_width) - .attr('d', line); - - // Overlay area for the mouseover events - chart.append('rect') - .attr('class', 'prometheus-graph-overlay') - .attr('width', this.width) - .attr('height', this.height) - .on('mousemove', this.handleMouseOverGraph.bind(this, prometheusGraphContainer)); - } - - // The legends from the metric - createAxisLabelContainers(axisLabelContainer, key) { - const graphSpecifics = this.graphSpecificProperties[key]; - - axisLabelContainer.append('line') - .attr('class', 'label-x-axis-line') - .attr('stroke', '#000000') - .attr('stroke-width', '1') - .attr({ - x1: 10, - y1: this.originalHeight - this.margin.top, - x2: (this.originalWidth - this.margin.right) + 10, - y2: this.originalHeight - this.margin.top, - }); - - axisLabelContainer.append('line') - .attr('class', 'label-y-axis-line') - .attr('stroke', '#000000') - .attr('stroke-width', '1') - .attr({ - x1: 10, - y1: 0, - x2: 10, - y2: this.originalHeight - this.margin.top, - }); - - axisLabelContainer.append('rect') - .attr('class', 'rect-axis-text') - .attr('x', 0) - .attr('y', 50) - .attr('width', 30) - .attr('height', 150); - - axisLabelContainer.append('text') - .attr('class', 'label-axis-text') - .attr('text-anchor', 'middle') - .attr('transform', `translate(15, ${(this.originalHeight - this.margin.top) / 2}) rotate(-90)`) - .text(graphSpecifics.graph_legend_title); - - axisLabelContainer.append('rect') - .attr('class', 'rect-axis-text') - .attr('x', (this.originalWidth / 2) - this.margin.right) - .attr('y', this.originalHeight - 100) - .attr('width', 30) - .attr('height', 80); - - axisLabelContainer.append('text') - .attr('class', 'label-axis-text') - .attr('x', (this.originalWidth / 2) - this.margin.right) - .attr('y', this.originalHeight - this.margin.top) - .attr('dy', '.35em') - .text('Time'); - - // Legends - - // Metric Usage - axisLabelContainer.append('rect') - .attr('x', this.originalWidth - 170) - .attr('y', (this.originalHeight / 2) - 60) - .style('fill', graphSpecifics.area_fill_color) - .attr('width', 20) - .attr('height', 35); - - axisLabelContainer.append('text') - .attr('class', 'text-metric-title') - .attr('x', this.originalWidth - 140) - .attr('y', (this.originalHeight / 2) - 50) - .text('Average'); - - axisLabelContainer.append('text') - .attr('class', 'text-metric-usage') - .attr('x', this.originalWidth - 140) - .attr('y', (this.originalHeight / 2) - 25); - } - - handleMouseOverGraph(prometheusGraphContainer) { - const rectOverlay = document.querySelector(`${prometheusGraphContainer} .prometheus-graph-overlay`); - const currentXCoordinate = d3.mouse(rectOverlay)[0]; - - Object.keys(this.graphSpecificProperties).forEach((key) => { - const currentGraphProps = this.graphSpecificProperties[key]; - const timeValueOverlay = currentGraphProps.xScale.invert(currentXCoordinate); - const overlayIndex = bisectDate(currentGraphProps.data, timeValueOverlay, 1); - const d0 = currentGraphProps.data[overlayIndex - 1]; - const d1 = currentGraphProps.data[overlayIndex]; - const evalTime = timeValueOverlay - d0.time > d1.time - timeValueOverlay; - const currentData = evalTime ? d1 : d0; - const currentTimeCoordinate = Math.floor(currentGraphProps.xScale(currentData.time)); - const currentDeployXPos = this.deployments.mouseOverDeployInfo(currentXCoordinate, key); - const currentPrometheusGraphContainer = `${prometheusGraphsContainer}[graph-type=${key}]`; - const maxValueFromData = d3.max(currentGraphProps.data.map(metricValue => metricValue.value)); - const maxMetricValue = currentGraphProps.yScale(maxValueFromData); - - // Clear up all the pieces of the flag - d3.selectAll(`${currentPrometheusGraphContainer} .selected-metric-line`).remove(); - d3.selectAll(`${currentPrometheusGraphContainer} .circle-metric`).remove(); - d3.selectAll(`${currentPrometheusGraphContainer} .rect-text-metric:not(.deploy-info-rect)`).remove(); - - const currentChart = d3.select(currentPrometheusGraphContainer).select('g'); - currentChart.append('line') - .attr({ - class: `${currentDeployXPos ? 'hidden' : ''} selected-metric-line`, - x1: currentTimeCoordinate, - y1: currentGraphProps.yScale(0), - x2: currentTimeCoordinate, - y2: maxMetricValue, - }); - - currentChart.append('circle') - .attr('class', 'circle-metric') - .attr('fill', currentGraphProps.line_color) - .attr('cx', currentDeployXPos || currentTimeCoordinate) - .attr('cy', currentGraphProps.yScale(currentData.value)) - .attr('r', this.commonGraphProperties.circle_radius_metric); - - if (currentDeployXPos) return; - - // The little box with text - const rectTextMetric = currentChart.append('svg') - .attr({ - class: 'rect-text-metric', - x: currentTimeCoordinate, - y: 0, - }); - - rectTextMetric.append('rect') - .attr({ - class: 'rect-metric', - x: 4, - y: 1, - rx: 2, - width: this.commonGraphProperties.rect_text_width, - height: this.commonGraphProperties.rect_text_height, - }); - - rectTextMetric.append('text') - .attr({ - class: 'text-metric text-metric-bold', - x: 8, - y: 35, - }) - .text(timeFormat(currentData.time)); - - rectTextMetric.append('text') - .attr({ - class: 'text-metric-date', - x: 8, - y: 15, - }) - .text(dateFormat(currentData.time)); - - let currentMetricValue = formatRelevantDigits(currentData.value); - if (key === 'cpu_values') { - currentMetricValue = `${currentMetricValue}%`; - } else { - currentMetricValue = `${currentMetricValue} MB`; - } - - d3.select(`${currentPrometheusGraphContainer} .text-metric-usage`) - .text(currentMetricValue); - }); - } - - configureGraph() { - this.graphSpecificProperties = { - cpu_values: { - area_fill_color: '#edf3fc', - line_color: '#5b99f7', - graph_legend_title: 'CPU Usage (Cores)', - data: [], - xScale: {}, - yScale: {}, - }, - memory_values: { - area_fill_color: '#fca326', - line_color: '#fc6d26', - graph_legend_title: 'Memory Usage (MB)', - data: [], - xScale: {}, - yScale: {}, - }, - }; - - this.commonGraphProperties = { - area_stroke_width: 2, - median_total_characters: 8, - circle_radius_metric: 5, - rect_text_width: 90, - rect_text_height: 40, - axis_no_ticks: 3, - }; - } - - getData() { - const maxNumberOfRequests = 3; - this.state = '.js-loading'; - this.updateState(); - return gl.utils.backOff((next, stop) => { - $.ajax({ - url: metricsEndpoint, - dataType: 'json', - }) - .done((data, statusText, resp) => { - if (resp.status === statusCodes.NO_CONTENT) { - this.backOffRequestCounter = this.backOffRequestCounter += 1; - if (this.backOffRequestCounter < maxNumberOfRequests) { - next(); - } else if (this.backOffRequestCounter >= maxNumberOfRequests) { - stop(new Error('loading')); - } - } else if (!data.success) { - stop(new Error('loading')); - } else { - stop({ - status: resp.status, - metrics: data, - }); - } - }).fail(stop); - }) - .then((resp) => { - if (resp.status === statusCodes.NO_CONTENT) { - return {}; - } - return resp.metrics; - }) - .catch(() => { - const prevState = this.state; - this.state = '.js-unable-to-connect'; - this.updateState(prevState); - }); - } - - transformData(metricsResponse) { - Object.keys(metricsResponse.metrics).forEach((key) => { - if (key === 'cpu_values' || key === 'memory_values') { - const metricValues = (metricsResponse.metrics[key])[0]; - this.graphSpecificProperties[key].data = metricValues.values.map(metric => ({ - time: new Date(metric[0] * 1000), - value: metric[1], - })); - } - }); - } - - updateState(prevState) { - const $statesContainer = $(prometheusStatesContainer); - $(prometheusParentGraphContainer).hide(); - if (prevState) { - $(`${prevState}`, $statesContainer).addClass('hidden'); - } - $(`${this.state}`, $statesContainer).removeClass('hidden'); - $(prometheusStatesContainer).show(); - } -} - -export default PrometheusGraph; |