blob: f70b97613d8eed8cd2b9953e7e06b4f086ba749e [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*
* Licensed under the Apache License, Version 2.0
* See accompanying LICENSE file.
*/
angular.module('dashboard')
.directive('metricsCharts', function () {
'use strict';
return {
restrict: 'E',
templateUrl: 'views/apps/streamingapp/metrics_charts.html',
scope: true, // inherit parent scope
controller: ['$scope', '$interval', 'helper', 'models', function ($scope, $interval, helper, models) {
'use strict';
var metricsProvider = $scope.dag;
var processorId;
$scope.chartGridClass = 'col-sm-12';
if ($scope.processor) {
processorId = $scope.processor.id;
$scope.chartGridClass = 'col-sm-6';
$scope.sendThroughputMetricsCaption = 'Message Send Throughput';
$scope.sendThroughputMetricsDescription = '';
$scope.receiveThroughputMetricsCaption = 'Message Receive Throughput';
$scope.receiveThroughputMetricsDescription = '';
$scope.messageLatencyMetricsCaption = 'Average Message Receive Latency';
$scope.messageLatencyMetricsDescription = '';
}
var sc = $scope.metricsConfig;
var recentChartPoints = sc.retainRecentDataSeconds * 1000 / sc.retainRecentDataIntervalMs;
var histChartPoints = sc.retainHistoryDataHours * 3600 * 1000 / sc.retainHistoryDataIntervalMs;
recentChartPoints--; // ProcessorFilter will actually reduce one point
histChartPoints--;
var updateRecentMetricsPromise;
$scope.$on('$destroy', function () {
$interval.cancel(updateRecentMetricsPromise);
});
// part 1
function createChart(unit, visibleDataPointsNum, dataPoints) {
return {
options: {
height: '108px',
margin: {right: 30},
seriesNames: [''],
yAxisLabelFormatter: helper.yAxisLabelFormatterWithoutValue0(),
visibleDataPointsNum: visibleDataPointsNum,
data: dataPoints,
valueFormatter: function (value) {
return helper.readableMetricValue(value) + ' ' + unit;
}
},
data: null
};
}
function createThroughputChart(visibleDataPointsNum, dataPoints) {
return createChart('msg/s', visibleDataPointsNum, dataPoints);
}
function createDurationChart(visibleDataPointsNum, dataPoints) {
return createChart('ms', visibleDataPointsNum, dataPoints);
}
$scope.sendThroughputChart = createThroughputChart(recentChartPoints);
$scope.receiveThroughputChart = createThroughputChart(recentChartPoints);
$scope.averageProcessingTimeChart = createDurationChart(recentChartPoints);
$scope.messageReceiveLatencyChart = createDurationChart(recentChartPoints);
function redrawMetricsCharts() {
function _rebuildChartWithNewDataPoints(chartOptions, metrics) {
var all = !$scope.isShowingCurrentMetrics;
var dataPoints = metricsToChartData(metrics);
var visibleDataPointsNum = all ?
Math.max(dataPoints.length, histChartPoints) : recentChartPoints;
if (all && dataPoints.length < 2) {
// Hide chart data, if there is only one data point there, which looks very ugly.
visibleDataPointsNum = 0;
dataPoints = [];
}
// Rebuild the chart
chartOptions.data = dataPoints;
chartOptions.visibleDataPointsNum = visibleDataPointsNum;
}
if (!$scope.isShowingCurrentMetrics) {
$interval.cancel(updateRecentMetricsPromise);
}
var queryMetricsPromise = $scope.isShowingCurrentMetrics ?
models.$get.appMetrics($scope.app.appId, $scope.metricsConfig.retainRecentDataIntervalMs) :
models.$get.appHistMetrics($scope.app.appId);
queryMetricsPromise.then(function (metrics) {
var data = metrics.$data();
var timeResolution = $scope.isShowingCurrentMetrics ?
$scope.metricsConfig.retainRecentDataIntervalMs :
$scope.metricsConfig.retainHistoryDataIntervalMs;
metricsProvider.replaceHistoricalMetrics(data, timeResolution);
if (angular.isNumber(processorId)) {
_rebuildChartWithNewDataPoints($scope.sendThroughputChart.options,
metricsProvider.getProcessorHistoricalMessageSendThroughput(processorId));
_rebuildChartWithNewDataPoints($scope.receiveThroughputChart.options,
metricsProvider.getProcessorHistoricalMessageReceiveThroughput(processorId));
_rebuildChartWithNewDataPoints($scope.averageProcessingTimeChart.options,
metricsProvider.getProcessorHistoricalAverageMessageProcessingTime(processorId));
_rebuildChartWithNewDataPoints($scope.messageReceiveLatencyChart.options,
metricsProvider.getProcessorHistoricalAverageMessageReceiveLatency(processorId));
} else {
_rebuildChartWithNewDataPoints($scope.sendThroughputChart.options,
metricsProvider.getSourceProcessorHistoricalMessageSendThroughput());
_rebuildChartWithNewDataPoints($scope.receiveThroughputChart.options,
metricsProvider.getSinkProcessorHistoricalMessageReceiveThroughput());
_rebuildChartWithNewDataPoints($scope.messageReceiveLatencyChart.options,
metricsProvider.getHistoricalCriticalPathLatency());
}
if ($scope.isShowingCurrentMetrics) {
updateRecentMetricsPromise = $interval(fillChartsWithCurrentMetrics,
sc.retainRecentDataIntervalMs);
}
});
}
function fillChartsWithCurrentMetrics() {
function _data(metric, metricTime) {
var metrics = {};
metrics[metricTime] = [metric];
return metricsToChartData(metrics);
}
var timeResolution = $scope.metricsConfig.retainRecentDataIntervalMs;
var metricTime = Math.floor(metricsProvider.metricsUpdateTime / timeResolution) * timeResolution;
$scope.sendThroughputChart.data = _data($scope.currentMessageSendRate, metricTime);
$scope.receiveThroughputChart.data = _data($scope.currentMessageReceiveRate, metricTime);
$scope.averageProcessingTimeChart.data = _data($scope.averageProcessingTime, metricTime);
$scope.messageReceiveLatencyChart.data = _data($scope.messageReceiveLatency, metricTime);
}
function metricsToChartData(metrics) {
return _.map(metrics, function (value, timeString) {
return {
x: helper.timeToChartTimeLabel(Number(timeString), /*shortForm=*/$scope.isShowingCurrentMetrics),
y: helper.metricRounded(value)
};
});
}
$scope.isShowingCurrentMetrics = true;
$scope.$watch('isShowingCurrentMetrics', function (newVal, oldVal) {
if (angular.equals(newVal, oldVal)) {
return; // ignore initial notification
}
redrawMetricsCharts();
});
// common watching
var initial = true;
$scope.$watch('dag.metricsUpdateTime', function () {
if (initial) {
// note that, the latest metrics do not contain enough points for drawing charts, so
// we request recent metrics from server.
redrawMetricsCharts();
initial = false;
}
updateMetricCards();
});
function updateMetricCards() {
var sentMessages, receivedMessages;
if (angular.isNumber(processorId)) {
sentMessages = metricsProvider.getProcessorSentMessages(processorId);
receivedMessages = metricsProvider.getProcessorReceivedMessages(processorId);
$scope.averageProcessingTime = metricsProvider.getProcessorAverageMessageProcessingTime(processorId);
$scope.messageReceiveLatency = metricsProvider.getProcessorMessageReceiveLatency(processorId);
} else {
sentMessages = metricsProvider.getSourceProcessorSentMessageTotalAndRate();
receivedMessages = metricsProvider.getSinkProcessorReceivedMessageTotalAndRate();
$scope.messageReceiveLatency = metricsProvider.getCriticalPathLatency();
}
$scope.currentMessageSendRate = sentMessages.rate;
$scope.currentMessageReceiveRate = receivedMessages.rate;
$scope.totalSentMessages = sentMessages.total;
$scope.totalReceivedMessages = receivedMessages.total;
}
}]
};
})
;