open-consul/ui-v2/app/components/topology-metrics/series/index.js

235 lines
7.0 KiB
JavaScript
Raw Normal View History

Add metrics rendering to the new topology view. (#8858) * Remove unused StatsCard component * Create Card and Stats contextual components with styling * Send endpoint, item, and protocol to Stats as props * WIP basic plumbing for metrics in Ember * WIP metrics data source now works for different protocols and produces reasonable mock responses * WIP sparkline component * Mostly working metrics and graphs in topology * Fix date in tooltip to actually be correct * Clean up console.log * Add loading frame and create a style sheet for Stats * Various polish fixes: - Loading state for graph - Added fake latency cookie value to test loading - If metrics provider has no series/stats for the service show something that doesn't look broken - Graph hover works right to the edge now - Stats boxes now wrap so they are either shown or not as will fit not cut off - Graph resizes when browser window size changes - Some tweaks to number formats and stat metrics to make them more compact/useful * Thread Protocol through topology model correctly * Rebuild assetfs * Fix failing tests and remove stats-card now it's changed and become different * Fix merge conflict * Update api doublt * more merge fixes * Add data-permission and id attr to Card * Run JS linter * Move things around so the tests run with everything available * Get tests passing: 1. Remove fakeLatency setTimeout (will be replaced with CONSUL_LATENCY in mocks) 2. Make sure any event handlers are removed * Make sure the Consul/scripts are available before the app * Make sure interval gets set if there is no cookie value * Upgrade mocks so we can use CONSUL_LATENCY * Fix handling of no series values from Prometheus * Update assetfs and fix a comment * Rebase and rebuild assetfs; fix tcp metric series units to be bits not bytes * Rebuild assetfs * Hide stats when provider is not configured Co-authored-by: kenia <keniavalladarez@gmail.com> Co-authored-by: John Cowen <jcowen@hashicorp.com>
2020-10-09 20:31:15 +00:00
import Component from '@ember/component';
import dayjs from 'dayjs';
import Calendar from 'dayjs/plugin/calendar';
import { select, pointer } from 'd3-selection';
Add metrics rendering to the new topology view. (#8858) * Remove unused StatsCard component * Create Card and Stats contextual components with styling * Send endpoint, item, and protocol to Stats as props * WIP basic plumbing for metrics in Ember * WIP metrics data source now works for different protocols and produces reasonable mock responses * WIP sparkline component * Mostly working metrics and graphs in topology * Fix date in tooltip to actually be correct * Clean up console.log * Add loading frame and create a style sheet for Stats * Various polish fixes: - Loading state for graph - Added fake latency cookie value to test loading - If metrics provider has no series/stats for the service show something that doesn't look broken - Graph hover works right to the edge now - Stats boxes now wrap so they are either shown or not as will fit not cut off - Graph resizes when browser window size changes - Some tweaks to number formats and stat metrics to make them more compact/useful * Thread Protocol through topology model correctly * Rebuild assetfs * Fix failing tests and remove stats-card now it's changed and become different * Fix merge conflict * Update api doublt * more merge fixes * Add data-permission and id attr to Card * Run JS linter * Move things around so the tests run with everything available * Get tests passing: 1. Remove fakeLatency setTimeout (will be replaced with CONSUL_LATENCY in mocks) 2. Make sure any event handlers are removed * Make sure the Consul/scripts are available before the app * Make sure interval gets set if there is no cookie value * Upgrade mocks so we can use CONSUL_LATENCY * Fix handling of no series values from Prometheus * Update assetfs and fix a comment * Rebase and rebuild assetfs; fix tcp metric series units to be bits not bytes * Rebuild assetfs * Hide stats when provider is not configured Co-authored-by: kenia <keniavalladarez@gmail.com> Co-authored-by: John Cowen <jcowen@hashicorp.com>
2020-10-09 20:31:15 +00:00
import { scaleLinear, scaleTime, scaleOrdinal } from 'd3-scale';
import { schemeTableau10 } from 'd3-scale-chromatic';
import { area, stack, stackOrderReverse } from 'd3-shape';
import { max, extent, bisector } from 'd3-array';
dayjs.extend(Calendar);
function niceTimeWithSeconds(d) {
return dayjs(d).calendar(null, {
sameDay: '[Today at] h:mm:ss A',
lastDay: '[Yesterday at] h:mm:ss A',
lastWeek: '[Last] dddd at h:mm:ss A',
sameElse: 'MMM DD at h:mm:ss A',
});
}
export default Component.extend({
data: null,
actions: {
redraw: function(evt) {
this.drawGraphs();
},
change: function(evt) {
this.data = evt.data;
this.element.querySelector('.sparkline-loader').style.display = 'none';
this.drawGraphs();
},
},
drawGraphs: function() {
if (!this.data.series) {
return;
}
let svg = (this.svg = select(this.element.querySelector('svg.sparkline')));
svg.on('mouseover mousemove mouseout', null);
svg.selectAll('path').remove();
svg.selectAll('rect').remove();
let bb = svg.node().getBoundingClientRect();
let w = bb.width;
let h = bb.height;
// To be safe, filter any series that actually have no data points. This can
// happen thanks to our current provider contract allowing empty arrays for
// series data if there is no value.
//
// TODO(banks): switch series provider data to be a single array with series
// values as properties as we need below to enforce sensible alignment of
// timestamps and explicit summing expectations.
let series = ((this.data || {}).series || []).filter(s => s.data.length > 0);
if (series.length == 0) {
// Put the graph in an error state that might get fixed if metrics show up
// on next poll.
let loader = this.element.querySelector('.sparkline-loader');
loader.innerHTML = 'No Metrics Available';
loader.style.display = 'block';
return;
}
// Fill the timestamps for x axis.
let data = series[0].data.map(d => {
return { time: d[0] };
});
let keys = [];
// Initialize zeros
let summed = this.data.series[0].data.map(d => 0);
for (var i = 0; i < series.length; i++) {
let s = series[i];
// Attach the value as a new field to the data grid.
s.data.map((d, idx) => {
data[idx][s.label] = d[1];
summed[idx] += d[1];
});
keys.push(s.label);
}
let st = stack()
.keys(keys)
.order(stackOrderReverse);
let stackData = st(data);
let x = scaleTime()
.domain(extent(data, d => d.time))
.range([0, w]);
let y = scaleLinear()
.domain([0, max(summed)])
.range([h, 0]);
let a = area()
.x(d => x(d.data.time))
.y1(d => y(d[0]))
.y0(d => y(d[1]));
// Use the grey/red we prefer by default but have more colors available in
// case user adds extra series with a custom provider.
let colorScheme = ['#DCE0E6', '#C73445'].concat(schemeTableau10);
let color = scaleOrdinal(colorScheme).domain(keys);
svg
.selectAll('path')
.data(stackData)
.join('path')
.attr('fill', ({ key }) => color(key))
.attr('stroke', ({ key }) => color(key))
.attr('d', a);
let cursor = svg
.append('rect')
.attr('class', 'cursor')
.style('visibility', 'hidden')
.attr('width', 1)
.attr('height', h)
.attr('x', 0)
.attr('y', 0);
let tooltip = select(this.element.querySelector('.tooltip'));
tooltip.selectAll('.sparkline-tt-legend').remove();
for (var k of keys) {
let legend = tooltip.append('div').attr('class', 'sparkline-tt-legend');
legend
.append('div')
.attr('class', 'sparkline-tt-legend-color')
.style('background-color', color(k));
legend
.append('span')
.text(k + ': ')
.append('span')
.attr('class', 'sparkline-tt-legend-value');
}
let tipVals = tooltip.selectAll('.sparkline-tt-legend-value');
let self = this;
svg
.on('mouseover', function(e) {
Add metrics rendering to the new topology view. (#8858) * Remove unused StatsCard component * Create Card and Stats contextual components with styling * Send endpoint, item, and protocol to Stats as props * WIP basic plumbing for metrics in Ember * WIP metrics data source now works for different protocols and produces reasonable mock responses * WIP sparkline component * Mostly working metrics and graphs in topology * Fix date in tooltip to actually be correct * Clean up console.log * Add loading frame and create a style sheet for Stats * Various polish fixes: - Loading state for graph - Added fake latency cookie value to test loading - If metrics provider has no series/stats for the service show something that doesn't look broken - Graph hover works right to the edge now - Stats boxes now wrap so they are either shown or not as will fit not cut off - Graph resizes when browser window size changes - Some tweaks to number formats and stat metrics to make them more compact/useful * Thread Protocol through topology model correctly * Rebuild assetfs * Fix failing tests and remove stats-card now it's changed and become different * Fix merge conflict * Update api doublt * more merge fixes * Add data-permission and id attr to Card * Run JS linter * Move things around so the tests run with everything available * Get tests passing: 1. Remove fakeLatency setTimeout (will be replaced with CONSUL_LATENCY in mocks) 2. Make sure any event handlers are removed * Make sure the Consul/scripts are available before the app * Make sure interval gets set if there is no cookie value * Upgrade mocks so we can use CONSUL_LATENCY * Fix handling of no series values from Prometheus * Update assetfs and fix a comment * Rebase and rebuild assetfs; fix tcp metric series units to be bits not bytes * Rebuild assetfs * Hide stats when provider is not configured Co-authored-by: kenia <keniavalladarez@gmail.com> Co-authored-by: John Cowen <jcowen@hashicorp.com>
2020-10-09 20:31:15 +00:00
tooltip.style('visibility', 'visible');
cursor.style('visibility', 'visible');
// We update here since we might redraw the graph with user's cursor
// stationary over it. If that happens mouseover fires but not
// mousemove but the tooltip and cursor are wrong (based on old data).
self.updateTooltip(e, data, stackData, keys, x, tooltip, tipVals, cursor);
Add metrics rendering to the new topology view. (#8858) * Remove unused StatsCard component * Create Card and Stats contextual components with styling * Send endpoint, item, and protocol to Stats as props * WIP basic plumbing for metrics in Ember * WIP metrics data source now works for different protocols and produces reasonable mock responses * WIP sparkline component * Mostly working metrics and graphs in topology * Fix date in tooltip to actually be correct * Clean up console.log * Add loading frame and create a style sheet for Stats * Various polish fixes: - Loading state for graph - Added fake latency cookie value to test loading - If metrics provider has no series/stats for the service show something that doesn't look broken - Graph hover works right to the edge now - Stats boxes now wrap so they are either shown or not as will fit not cut off - Graph resizes when browser window size changes - Some tweaks to number formats and stat metrics to make them more compact/useful * Thread Protocol through topology model correctly * Rebuild assetfs * Fix failing tests and remove stats-card now it's changed and become different * Fix merge conflict * Update api doublt * more merge fixes * Add data-permission and id attr to Card * Run JS linter * Move things around so the tests run with everything available * Get tests passing: 1. Remove fakeLatency setTimeout (will be replaced with CONSUL_LATENCY in mocks) 2. Make sure any event handlers are removed * Make sure the Consul/scripts are available before the app * Make sure interval gets set if there is no cookie value * Upgrade mocks so we can use CONSUL_LATENCY * Fix handling of no series values from Prometheus * Update assetfs and fix a comment * Rebase and rebuild assetfs; fix tcp metric series units to be bits not bytes * Rebuild assetfs * Hide stats when provider is not configured Co-authored-by: kenia <keniavalladarez@gmail.com> Co-authored-by: John Cowen <jcowen@hashicorp.com>
2020-10-09 20:31:15 +00:00
})
.on('mousemove', function(e, d, i) {
self.updateTooltip(e, data, stackData, keys, x, tooltip, tipVals, cursor);
Add metrics rendering to the new topology view. (#8858) * Remove unused StatsCard component * Create Card and Stats contextual components with styling * Send endpoint, item, and protocol to Stats as props * WIP basic plumbing for metrics in Ember * WIP metrics data source now works for different protocols and produces reasonable mock responses * WIP sparkline component * Mostly working metrics and graphs in topology * Fix date in tooltip to actually be correct * Clean up console.log * Add loading frame and create a style sheet for Stats * Various polish fixes: - Loading state for graph - Added fake latency cookie value to test loading - If metrics provider has no series/stats for the service show something that doesn't look broken - Graph hover works right to the edge now - Stats boxes now wrap so they are either shown or not as will fit not cut off - Graph resizes when browser window size changes - Some tweaks to number formats and stat metrics to make them more compact/useful * Thread Protocol through topology model correctly * Rebuild assetfs * Fix failing tests and remove stats-card now it's changed and become different * Fix merge conflict * Update api doublt * more merge fixes * Add data-permission and id attr to Card * Run JS linter * Move things around so the tests run with everything available * Get tests passing: 1. Remove fakeLatency setTimeout (will be replaced with CONSUL_LATENCY in mocks) 2. Make sure any event handlers are removed * Make sure the Consul/scripts are available before the app * Make sure interval gets set if there is no cookie value * Upgrade mocks so we can use CONSUL_LATENCY * Fix handling of no series values from Prometheus * Update assetfs and fix a comment * Rebase and rebuild assetfs; fix tcp metric series units to be bits not bytes * Rebuild assetfs * Hide stats when provider is not configured Co-authored-by: kenia <keniavalladarez@gmail.com> Co-authored-by: John Cowen <jcowen@hashicorp.com>
2020-10-09 20:31:15 +00:00
})
.on('mouseout', function(e) {
Add metrics rendering to the new topology view. (#8858) * Remove unused StatsCard component * Create Card and Stats contextual components with styling * Send endpoint, item, and protocol to Stats as props * WIP basic plumbing for metrics in Ember * WIP metrics data source now works for different protocols and produces reasonable mock responses * WIP sparkline component * Mostly working metrics and graphs in topology * Fix date in tooltip to actually be correct * Clean up console.log * Add loading frame and create a style sheet for Stats * Various polish fixes: - Loading state for graph - Added fake latency cookie value to test loading - If metrics provider has no series/stats for the service show something that doesn't look broken - Graph hover works right to the edge now - Stats boxes now wrap so they are either shown or not as will fit not cut off - Graph resizes when browser window size changes - Some tweaks to number formats and stat metrics to make them more compact/useful * Thread Protocol through topology model correctly * Rebuild assetfs * Fix failing tests and remove stats-card now it's changed and become different * Fix merge conflict * Update api doublt * more merge fixes * Add data-permission and id attr to Card * Run JS linter * Move things around so the tests run with everything available * Get tests passing: 1. Remove fakeLatency setTimeout (will be replaced with CONSUL_LATENCY in mocks) 2. Make sure any event handlers are removed * Make sure the Consul/scripts are available before the app * Make sure interval gets set if there is no cookie value * Upgrade mocks so we can use CONSUL_LATENCY * Fix handling of no series values from Prometheus * Update assetfs and fix a comment * Rebase and rebuild assetfs; fix tcp metric series units to be bits not bytes * Rebuild assetfs * Hide stats when provider is not configured Co-authored-by: kenia <keniavalladarez@gmail.com> Co-authored-by: John Cowen <jcowen@hashicorp.com>
2020-10-09 20:31:15 +00:00
tooltip.style('visibility', 'hidden');
cursor.style('visibility', 'hidden');
});
},
willDestroyElement: function() {
this._super(...arguments);
if (typeof this.svg !== 'undefined') {
this.svg.on('mouseover mousemove mouseout', null);
}
},
updateTooltip: function(e, data, stackData, keys, x, tooltip, tipVals, cursor) {
let [mouseX] = pointer(e);
Add metrics rendering to the new topology view. (#8858) * Remove unused StatsCard component * Create Card and Stats contextual components with styling * Send endpoint, item, and protocol to Stats as props * WIP basic plumbing for metrics in Ember * WIP metrics data source now works for different protocols and produces reasonable mock responses * WIP sparkline component * Mostly working metrics and graphs in topology * Fix date in tooltip to actually be correct * Clean up console.log * Add loading frame and create a style sheet for Stats * Various polish fixes: - Loading state for graph - Added fake latency cookie value to test loading - If metrics provider has no series/stats for the service show something that doesn't look broken - Graph hover works right to the edge now - Stats boxes now wrap so they are either shown or not as will fit not cut off - Graph resizes when browser window size changes - Some tweaks to number formats and stat metrics to make them more compact/useful * Thread Protocol through topology model correctly * Rebuild assetfs * Fix failing tests and remove stats-card now it's changed and become different * Fix merge conflict * Update api doublt * more merge fixes * Add data-permission and id attr to Card * Run JS linter * Move things around so the tests run with everything available * Get tests passing: 1. Remove fakeLatency setTimeout (will be replaced with CONSUL_LATENCY in mocks) 2. Make sure any event handlers are removed * Make sure the Consul/scripts are available before the app * Make sure interval gets set if there is no cookie value * Upgrade mocks so we can use CONSUL_LATENCY * Fix handling of no series values from Prometheus * Update assetfs and fix a comment * Rebase and rebuild assetfs; fix tcp metric series units to be bits not bytes * Rebuild assetfs * Hide stats when provider is not configured Co-authored-by: kenia <keniavalladarez@gmail.com> Co-authored-by: John Cowen <jcowen@hashicorp.com>
2020-10-09 20:31:15 +00:00
cursor.attr('x', mouseX);
let mouseTime = x.invert(mouseX);
var bisectTime = bisector(function(d) {
return d.time;
}).left;
let tipIdx = bisectTime(data, mouseTime);
tooltip
// 22 px is the correction to align the arrow on the tool tip with
// cursor.
.style('left', mouseX - 22 + 'px')
.select('.sparkline-time')
.text(niceTimeWithSeconds(mouseTime));
tipVals.nodes().forEach((n, i) => {
let val = stackData[i][tipIdx][1] - stackData[i][tipIdx][0];
select(n).text(this.formatTooltip(keys[i], val));
});
cursor.attr('x', mouseX);
},
formatTooltip: function(label, val) {
switch (label) {
case 'Data rate received':
// fallthrough
case 'Data rate transmitted':
return dataRateStr(val);
default:
return shortNumStr(val);
}
},
});
// Duplicated in vendor/metrics-providers/prometheus.js since we want that to
// remain a standalone example of a provider that could be loaded externally.
function shortNumStr(n) {
if (n < 1e3) {
if (Number.isInteger(n)) return '' + n;
if (n >= 100) {
// Go to 3 significant figures but wrap it in Number to avoid scientific
// notation lie 2.3e+2 for 230.
return Number(n.toPrecision(3));
}
if (n < 1) {
// Very small numbers show with limited precision to prevent long string
// of 0.000000.
return Number(n.toFixed(2));
} else {
// Two sig figs is enough below this
return Number(n.toPrecision(2));
}
}
if (n >= 1e3 && n < 1e6) return +(n / 1e3).toPrecision(3) + 'k';
if (n >= 1e6 && n < 1e9) return +(n / 1e6).toPrecision(3) + 'm';
if (n >= 1e9 && n < 1e12) return +(n / 1e9).toPrecision(3) + 'g';
if (n >= 1e12) return +(n / 1e12).toFixed(0) + 't';
}
function dataRateStr(n) {
return shortNumStr(n) + 'bps';
}