diff --git a/src/ServicePulse.Host/app/css/particular.css b/src/ServicePulse.Host/app/css/particular.css
index 331529894f..cfd97653ea 100644
--- a/src/ServicePulse.Host/app/css/particular.css
+++ b/src/ServicePulse.Host/app/css/particular.css
@@ -3526,4 +3526,58 @@ g.error .node-text .lead.saga {
g.error .node-text a:hover {
text-decoration: underline;
+}
+
+:root {
+ --avg-tooltip-background-color: #000;
+ }
+
+div.avg-tooltip {
+ position: absolute;
+ text-align: left;
+ padding: .5rem;
+ line-height: 1;
+ background: var(--avg-tooltip-background-color);
+ color: #ffffff;
+ border-radius: 8px 1px 1px 8px;
+ pointer-events: none;
+ font-size: 11px;
+ white-space: nowrap;
+}
+
+div.avg-tooltip.left {
+ border-radius: 1px 8px 8px 1px;
+}
+
+div.avg-tooltip:before {
+ content: '';
+ display: block;
+ z-index: -1;
+ right: 0;
+ position: absolute;
+ top: 50%;
+ background-color: var(--avg-tooltip-background-color);
+ width: 24px;
+ height: 24px;
+ margin-top: -12px;
+ margin-right: -12px;
+
+ transform: rotate(45deg);
+}
+
+div.avg-tooltip.left:before {
+ right: inherit;
+ margin-right: inherit;
+ margin-left: -12px;
+ left:0;
+}
+
+div.avg-tooltip .value {
+ font-size: 14px;
+ font-weight: bold;
+}
+
+div.avg-tooltip .value span {
+ font-size: 11px;
+ font-weight: normal;
}
\ No newline at end of file
diff --git a/src/ServicePulse.Host/app/modules/monitoring/js/directives/ui.particular.arrowLabel.js b/src/ServicePulse.Host/app/modules/monitoring/js/directives/ui.particular.arrowLabel.js
new file mode 100644
index 0000000000..b377f50ebf
--- /dev/null
+++ b/src/ServicePulse.Host/app/modules/monitoring/js/directives/ui.particular.arrowLabel.js
@@ -0,0 +1,50 @@
+export default function ArrowLabel({ pointToTheLeft = false, caption = '' }) {
+
+ var div = document.createElement('div');
+ div.style.position = 'absolute';
+ div.style.zIndex = 10;
+ div.style.visibility = 'hidden';
+ div.classList = `avg-tooltip${pointToTheLeft && ' left' || ''}`;
+ div.innerHTML = `
+ ${caption}
+
+
+ 0
+
`;
+ document.body.appendChild(div);
+
+ return {
+ displayAt: function ({ x, y, color}) {
+ var lableDimensions = getComputedStyle(div);
+ //align the label vertically.
+ div.style.top = `${Math.trunc(y - lableDimensions.height.replace('px', '') / 2)}px`;
+
+ //align the label horizontally.
+ //get the label tip dimensions. The label tip is composed by a roated square of the ::before pseudo-element.
+ var labelTipWidth = getComputedStyle(div, ':before').width.replace('px', '');
+ var labelTipHeight = getComputedStyle(div, ':before').height.replace('px', '');
+ var lalbeTipHypotenuse = Math.trunc(Math.hypot(labelTipWidth, labelTipHeight));
+
+ if (pointToTheLeft == false) {
+ div.style.left = 'inherit';
+ div.style.right = `calc(100% - ${x}px + ${lalbeTipHypotenuse / 2}px)`;
+ } else {
+ div.style.right = 'inherit';
+ div.style.left = `${x + (lalbeTipHypotenuse / 2)}px`;
+ }
+
+ div.style.visibility = 'visible';
+ div.style.setProperty('--avg-tooltip-background-color', color); //by using properties the color of the 'before' content pseudo element can be updated.
+ },
+
+ hide: function () {
+ div.style.visibility = 'hidden';
+ },
+
+ value: function (value, unit) {
+ div.querySelector('.value').innerHTML = `${value} ${unit}`;
+ },
+
+ pointingToTheLeft: pointToTheLeft
+ };
+}
\ No newline at end of file
diff --git a/src/ServicePulse.Host/app/modules/monitoring/js/directives/ui.particular.graph.js b/src/ServicePulse.Host/app/modules/monitoring/js/directives/ui.particular.graph.js
index be903726fb..3cd031d72e 100644
--- a/src/ServicePulse.Host/app/modules/monitoring/js/directives/ui.particular.graph.js
+++ b/src/ServicePulse.Host/app/modules/monitoring/js/directives/ui.particular.graph.js
@@ -1,18 +1,33 @@
-(function(window, angular, d3) {
+import ArrowLabel from './ui.particular.arrowLabel';
+
+(function(window, angular, d3) {
'use strict';
+ const averageDecimalsDefault = 2;
+ const avgLabelColorDefault = '#2700CB';
+ const avgLabelSuffixDefault = '';
+
+ var averageLabelToTheRight = ArrowLabel({pointToTheLeft: false, caption: 'AVG'});
+
+
angular.module('ui.particular.graph', [])
.directive('graph',
- function() {
+ function(formatter) {
return {
restrict: 'E',
scope: {
plotData: '=',
formatter: '&',
- minimumYaxis: '@'
+ minimumYaxis: '@',
+ isDurationGraph: '=isDurationGraph',
+ avgDecimals: '@'
},
template: '',
link: function link(scope, element, attrs) {
+ attrs.avgLabelColor = attrs.avgLabelColor || avgLabelColorDefault;
+ attrs.metricSuffix = attrs.metricSuffix || avgLabelSuffixDefault;
+ scope.avgDecimals = scope.avgDecimals || averageDecimalsDefault;
+
scope.plotData = scope.plotData || { points: [], average: 0 };
scope.$watch('plotData',
@@ -92,13 +107,39 @@
.attr('class', 'graph-data-line');
}
- chart.append('path')
+ var averageLine = chart.append('path')
.datum(Array(numberOfPoints).fill(average))
.attr('d', line)
.attr('class', 'graph-avg-line');
+
+ var displayAverageLabel = function(averageLine, label, value, color, unit) {
+ var {x, y, width} = averageLine.node().getBoundingClientRect();
+ label.value(value, unit);
+
+ if (label.pointingToTheLeft) {
+ label.displayAt({x:x + width + window.pageXOffset, y:y + window.pageYOffset, color});
+ } else {
+ label.displayAt({x:x + window.pageXOffset, y:y + window.pageYOffset, color});
+ }
+ }
+
+ chart.on("mouseover", function() {
+ var value = `${formatter.formatLargeNumber(average,scope.avgDecimals)}`;
+ var suffix = attrs.metricSuffix;
+
+ if (scope.isDurationGraph) {
+ value = `${formatter.formatTime(average).value}`;
+ suffix = formatter.formatTime(average).unit.toUpperCase();
+ }
+
+ displayAverageLabel(averageLine, averageLabelToTheRight, value, attrs.avgLabelColor, suffix);
+ })
+ .on("mouseout", function(){
+ averageLabelToTheRight.hide();
+ });
});
}
};
- });
+ });
}(window, window.angular, window.d3));
\ No newline at end of file
diff --git a/src/ServicePulse.Host/app/modules/monitoring/js/directives/ui.particular.largeGraph.js b/src/ServicePulse.Host/app/modules/monitoring/js/directives/ui.particular.largeGraph.js
index 6f08070f2d..6350ced1a5 100644
--- a/src/ServicePulse.Host/app/modules/monitoring/js/directives/ui.particular.largeGraph.js
+++ b/src/ServicePulse.Host/app/modules/monitoring/js/directives/ui.particular.largeGraph.js
@@ -1,6 +1,14 @@
-(function(window, angular, d3) {
+import ArrowLabel from './ui.particular.arrowLabel';
+
+(function(window, angular, d3) {
'use strict';
+ const averageDecimalsDefault = 2;
+ const avgLabelSuffixDefault = '';
+
+ var averageLabelToTheRight = ArrowLabel({pointToTheLeft: false, caption: 'AVG'});
+ var averageLabelToTheLeft = ArrowLabel({pointToTheLeft: true, caption: 'AVG'});
+
function drawDataSeries(chart, data, color, fillColor, scaleX, scaleY) {
var area = d3.area()
@@ -40,13 +48,15 @@
var group = chart.append('g').attr('class', 'dataAverage');
- group.append('path')
+ var avgLine = group.append('path')
.datum(Array(data.points.length).fill(data.average))
.attr('d', line)
.attr('stroke', color)
.attr('stroke-width', 1.5)
.attr('opacity', 0.5)
.attr('stroke-dasharray', '10,10');
+
+ return avgLine;
}
function padToWholeValue(value) {
@@ -78,10 +88,14 @@
isDurationGraph: '=isDurationGraph',
minimumYaxis: '@',
width: '=plotWidth',
- height: '=plotHeight'
+ height: '=plotHeight',
+ avgDecimals: '@'
},
template: '',
- link: function link(scope, element, attrs) {
+ link: function link(scope, element, attrs) {
+ scope.avgDecimals = scope.avgDecimals || averageDecimalsDefault;
+ attrs.metricSuffix = attrs.metricSuffix || avgLabelSuffixDefault;
+
scope.$watch('firstDataSeries', function () {
var svg = element.find('svg')[0];
@@ -163,7 +177,18 @@
}
var drawAverage = function(data, lineColor, fillColor) {
- drawAverageLine(chart, data, lineColor, fillColor, scaleX, scaleY);
+ return drawAverageLine(chart, data, lineColor, fillColor, scaleX, scaleY);
+ }
+
+ var displayAverageLabel = function(averageLine, label, value, color, unit) {
+ var {x, y, width} = averageLine.node().getBoundingClientRect();
+ label.value(value, unit);
+
+ if (label.pointingToTheLeft) {
+ label.displayAt({x:x + width + window.pageXOffset, y:y + window.pageYOffset, color});
+ } else {
+ label.displayAt({x:x + window.pageXOffset, y:y + window.pageYOffset, color});
+ }
}
drawSeries(firstSeries, attrs.firstSeriesColor, attrs.firstSeriesFillColor);
@@ -172,14 +197,43 @@
drawSeries(secondSeries, attrs.secondSeriesColor,attrs.secondSeriesFillColor);
}
- drawAverage(firstSeries, attrs.firstSeriesColor, attrs.firstSeriesFillColor );
+ var firstAverageLine = drawAverage(firstSeries, attrs.firstSeriesColor, attrs.firstSeriesFillColor );
+
+ var secondAverageLine = null;
if (secondSeries) {
- drawAverage(secondSeries, attrs.secondSeriesColor, attrs.secondSeriesFillColor);
+ secondAverageLine = drawAverage(secondSeries, attrs.secondSeriesColor, attrs.secondSeriesFillColor);
}
+
+ chart.on("mouseover", function() {
+ var value = `${formatter.formatLargeNumber(firstSeries.average, scope.avgDecimals)}`;
+ var suffix = attrs.metricSuffix;
+
+ if (scope.isDurationGraph) {
+ value = `${formatter.formatTime(firstSeries.average).value}`;
+ suffix = formatter.formatTime(firstSeries.average).unit.toUpperCase();
+ }
+
+ displayAverageLabel(firstAverageLine, averageLabelToTheRight, value, attrs.firstSeriesColor, suffix);
+
+ if (secondAverageLine && secondSeries.points.length > 0) {
+ value = `${formatter.formatLargeNumber(secondSeries.average, scope.avgDecimals)}`;
+
+ if (scope.isDurationGraph) {
+ value = `${formatter.formatTime(secondSeries.average).value}`;
+ suffix = formatter.formatTime(secondSeries.average).unit.toUpperCase();
+ }
+
+ displayAverageLabel(secondAverageLine, averageLabelToTheLeft, value, attrs.secondSeriesColor, suffix);
+ }
+ })
+ .on("mouseout", function() {
+ averageLabelToTheRight.hide();
+ averageLabelToTheLeft.hide();
+ });
});
}
};
});
-}(window, window.angular, window.d3));
+}(window, window.angular, window.d3));
\ No newline at end of file
diff --git a/src/ServicePulse.Host/app/modules/monitoring/views/endpoint_details.html b/src/ServicePulse.Host/app/modules/monitoring/views/endpoint_details.html
index 1485364f17..a0a014b456 100644
--- a/src/ServicePulse.Host/app/modules/monitoring/views/endpoint_details.html
+++ b/src/ServicePulse.Host/app/modules/monitoring/views/endpoint_details.html
@@ -49,6 +49,8 @@
plot-height="200"
first-series-color="#EA7E00"
first-series-fill-color="#EADDCE"
+ avg-decimals="0"
+ metric-suffix="MSGS"
class="large-graph pull-left">
@@ -87,7 +89,8 @@
first-series-color="#176397"
first-series-fill-color="#CADCE8"
second-series-color="#CC1252"
- second-series-fill-color="#E9C4D1"
+ second-series-fill-color="#E9C4D1"
+ metric-suffix="MSGS/S"
class="large-graph pull-left">
@@ -147,7 +150,7 @@
first-series-color="#2700CB"
first-series-fill-color="#C4BCE5"
second-series-color="#258135"
- second-series-fill-color="#BEE6C5"
+ second-series-fill-color="#BEE6C5"
is-duration-graph="true"
class="large-graph pull-left">
@@ -288,7 +291,7 @@