ui.js.flute.flute-app.js Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2016 Aitu Software Limited.
*
* Licensed 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.
*/
var METRIC_NAME_TO_VALUE_INDEX = {};
METRIC_NAME_TO_VALUE_INDEX["TIMESTAMP"] = 0;
METRIC_NAME_TO_VALUE_INDEX["MIN"] = 1;
METRIC_NAME_TO_VALUE_INDEX["MEAN"] = 2;
METRIC_NAME_TO_VALUE_INDEX["FIFTIETH"] = 3;
METRIC_NAME_TO_VALUE_INDEX["NINETIETH"] = 4;
METRIC_NAME_TO_VALUE_INDEX["TWO_NINES"] = 5;
METRIC_NAME_TO_VALUE_INDEX["THREE_NINES"] = 6;
METRIC_NAME_TO_VALUE_INDEX["FOUR_NINES"] = 7;
METRIC_NAME_TO_VALUE_INDEX["FIVE_NINES"] = 8;
METRIC_NAME_TO_VALUE_INDEX["MAX"] = 9;
METRIC_NAME_TO_VALUE_INDEX["COUNT"] = 10;
var METRIC_NAME_TO_DISPLAY_NAME = {};
METRIC_NAME_TO_DISPLAY_NAME["MIN"] = 'Min';
METRIC_NAME_TO_DISPLAY_NAME["MEAN"] = 'Mean';
METRIC_NAME_TO_DISPLAY_NAME["FIFTIETH"] = '50th';
METRIC_NAME_TO_DISPLAY_NAME["NINETIETH"] = '90th';
METRIC_NAME_TO_DISPLAY_NAME["TWO_NINES"] = '99th';
METRIC_NAME_TO_DISPLAY_NAME["THREE_NINES"] = '99.9th';
METRIC_NAME_TO_DISPLAY_NAME["FOUR_NINES"] = '99.99th';
METRIC_NAME_TO_DISPLAY_NAME["FIVE_NINES"] = '99.999th';
METRIC_NAME_TO_DISPLAY_NAME["MAX"] = 'Max';
METRIC_NAME_TO_DISPLAY_NAME["COUNT"] = 'Count';
(function outer_init() {
var reportMode = false;
var reportName = null;
var currentReportConfig = null;
var displayPercentileCharts = true;
function flute_init() {
d3.selectAll('.status').text('No report specified.');
var reportNameMatch = /.*(\?|&)report\=([^&]+)/.exec(window.location.search);
var metricNameMatch = /.*(\?|&)metric\=([^&]+)/.exec(window.location.search);
var endTimestampMatch = /.*(\?|&)endTimestamp\=([^&]+)/.exec(window.location.search);
if(reportNameMatch !== null) {
reportName = reportNameMatch[2];
fluteUtil.get('../../report/spec/' + reportName,
function(reportConfig) {
d3.selectAll('.status').text('Retrieved reporting config.');
d3.select('#headerReportName').text(reportName);
reportConfig.endTimestamp = null;
currentReportConfig = reportConfig;
createApp(reportConfig);
setInterval(reloadReportConfig, 5000);
},
function(statusCode) {
d3.selectAll('.status').text('Failed to retrieve config for report ' + reportName + ' code was ' + statusCode);
}
);
reportMode = true;
} else if(metricNameMatch !== null) {
var reportConfigArray = [];
var reportConfig = {};
reportConfig.unit = 'MICROSECONDS';
reportConfig.reportWindows = [];
reportConfig.reportWindows.push({unit: "HOURS", duration: 1});
reportConfig.metricThresholds = [];
reportConfig.metricThresholds.push({metricKey: metricNameMatch[2], metrics: [{name: 'MAX', value: 1000000}]});
reportConfig.endTimestamp = endTimestampMatch == null ? null : endTimestampMatch[2];
d3.selectAll('.status').text('Dynamic reporting config.');
d3.select('#headerReportName').text('Dynamic');
reportConfigArray.push(reportConfig);
currentReportConfig = reportConfigArray;
createApp(reportConfigArray);
}
}
function reloadReportConfig() {
fluteUtil.get('../../report/spec/' + reportName,
function(reportConfig) {
if(reportConfig[0].metricThresholds.length !== currentReportConfig[0].metricThresholds.length) {
window.location.reload();
}
},
function(statusCode) {
d3.selectAll('.status').text('Failed to retrieve config for report ' + reportName + ' code was ' + statusCode);
}
);
}
function createApp(reportingConfig) {
console.log(reportingConfig);
var fluteApplication = configureApplication(reportingConfig);
var view = createView();
view.layout(fluteApplication.getMetrics(),
getLayoutProperties(fluteApplication.getMetrics(), fluteApplication.getReportWindows()),
fluteApplication.getReportWindows());
fluteApplication.poll(view);
setInterval(function() {
fluteApplication.poll(view);
}, 5000);
}
function getLayoutProperties(metrics, reportWindows) {
var maxThresholdCountInAnyMetric = 0;
for(var i = 0; i < metrics.length; i++) {
var metricThresholdCount = metrics[i].getThresholdNames().length;
maxThresholdCountInAnyMetric = Math.max(metricThresholdCount, maxThresholdCountInAnyMetric);
}
return {
columnCount: maxThresholdCountInAnyMetric + 1,
rowCount: (metrics.length + 2) * reportWindows.length
}
}
function configureApplication(reportingConfig) {
return {
metricThresholds: reportingConfig[0].metricThresholds,
reportWindows: reportingConfig[0].reportWindows,
metrics: [],
getMetrics: function() {
if(this.metrics.length === 0) {
for(var i = 0; i < this.metricThresholds.length; i++) {
this.metrics.push(toMetric(this.metricThresholds[i]));
}
}
return this.metrics;
},
getReportWindows: function() {
return this.reportWindows;
},
poll: function(view) {
var currentTime = new Date().getTime();
var globalCounters = {};
for(var m = 0; m < this.getMetrics().length; m++) {
var metric = this.getMetrics()[m];
globalCounters[metric.getNormalisedName()] = {};
globalCounters[metric.getNormalisedName()]['thresholdExceededInAnyReportWindow'] = false;
globalCounters[metric.getNormalisedName()]['remainingReportWindowCount'] = this.reportWindows.length;
globalCounters[metric.getNormalisedName()]['remainingPercentileDataCount'] = this.reportWindows.length;
globalCounters[metric.getNormalisedName()]['percentileData'] = [];
for(var r = 0; r < this.reportWindows.length; r++) {
var reportWindow = this.reportWindows[r];
var url = '../../query/slaReport/' +
metric.metricName + '/' +
reportWindow.duration + '/' +
reportWindow.unit;
var percentileUrl = chartFunctions.getDataUrl(metric, reportWindow, reportingConfig.endTimestamp);
(function (boundMetric, reportWindow, url, index) {
d3.json(url, function(data) {
globalCounters[boundMetric.getNormalisedName()]['remainingPercentileDataCount']--;
var percentileData = globalCounters[boundMetric.getNormalisedName()]['percentileData'];
if(data && data.length) {
percentileData.push({reportWindow: reportWindow.duration + '_' + reportWindow.unit, data: data, index: index});
}
if(globalCounters[boundMetric.getNormalisedName()]['remainingPercentileDataCount'] === 0) {
drawPercentileChart(percentileData, boundMetric);
}
});
})(metric, reportWindow, percentileUrl, r);
(function (boundMetric, reportWindow, url) {
d3.json(url, function(data) {
if(data && data.length !== 0) {
var dataSet = data[data.length - 1];
var allBelowThreshold = true;
// TODO decide whether to collapse rows
allBelowThreshold = false;
for(var i = 0; i < boundMetric.getMetricThresholds().length; i++) {
var threshold = boundMetric.getMetricThresholds()[i];
var value = parseInt(dataSet[METRIC_NAME_TO_VALUE_INDEX[threshold.name]]);
if(value >= boundMetric.getThresholdValue(threshold.name)) {
allBelowThreshold = false;
}
}
var normalisedName = boundMetric.getNormalisedName();
if(!allBelowThreshold) {
globalCounters[normalisedName]['thresholdExceededInAnyReportWindow'] = true;
}
globalCounters[normalisedName]['remainingReportWindowCount']--;
if(globalCounters[normalisedName]['remainingReportWindowCount'] === 0) {
if(!globalCounters[normalisedName]['thresholdExceededInAnyReportWindow']) {
d3.selectAll('.row_' + normalisedName).
selectAll('td').text('');
} else {
for(var j = 0; j < boundMetric.getMetricThresholds().length; j++) {
var threshold = boundMetric.getMetricThresholds()[j];
var selector = '.threshold_' + normalisedName + '_' + threshold.name;
d3.selectAll(selector).text(METRIC_NAME_TO_DISPLAY_NAME[threshold.name]);
}
}
}
var reportWindowName = reportWindow.duration + '_' + reportWindow.unit;
var recordCount = dataSet[METRIC_NAME_TO_VALUE_INDEX['COUNT']];
if(recordCount === 0) {
indicateNoDataForCell(normalisedName, 'COUNT', reportWindowName);
for(var i = 0; i < boundMetric.getMetricThresholds().length; i++) {
var threshold = boundMetric.getMetricThresholds()[i];
indicateNoDataForCell(normalisedName, threshold.name, reportWindowName);
}
} else {
_updateCell(normalisedName, 'COUNT',
reportWindowName,
recordCount, Number.MAX_VALUE, !allBelowThreshold);
for(var i = 0; i < boundMetric.getMetricThresholds().length; i++) {
var threshold = boundMetric.getMetricThresholds()[i];
var value = parseInt(dataSet[METRIC_NAME_TO_VALUE_INDEX[threshold.name]]);
_updateCell(normalisedName, threshold.name,
reportWindowName, value,
boundMetric.getThresholdValue(threshold.name), !allBelowThreshold);
}
if(allBelowThreshold) {
d3.selectAll('.label_' + normalisedName + '_' + reportWindowName).
text('');
} else {
d3.selectAll('.label_' + normalisedName + '_' + reportWindowName).
text(windowDurationToDisplayText(reportWindow));
}
}
}
});
})(metric, reportWindow, url);
}
}
}
}
}
function drawPercentileChart(percentileData, boundMetric) {
chartFunctions.draw(percentileData, boundMetric);
}
function indicateNoDataForCell(normalisedName, thresholdName, reportWindowName) {
var valueCellId = '.value_' + reportWindowName + '_' + normalisedName + '_' + thresholdName;
d3.selectAll(valueCellId).classed('noDataReceived', true).
classed('thresholdNotExceeded', false).
classed('thresholdExceeded', false).
text('-');
}
function _updateCell(normalisedName, thresholdName, reportWindowName, actualValue, thresholdValue, shouldDisplayValue) {
var valueCellId = '.value_' + reportWindowName + '_' + normalisedName + '_' + thresholdName;
var cell = d3.selectAll(valueCellId);
var belowThreshold = actualValue < thresholdValue;
cell.text(shouldDisplayValue ? actualValue : '');
if(!shouldDisplayValue) {
cell.attr('class', 'collapsed');
} else {
cell.classed('thresholdExceeded', !belowThreshold);
cell.classed('thresholdNotExceeded', belowThreshold);
}
cell.classed('noDataReceived', false);
}
function windowDurationToDisplayText(reportWindow) {
var displayText = reportWindow.duration + ' ';
var pluralSuffix = 's';
if(reportWindow.unit === 'DAYS') {
displayText += 'day';
}
else if(reportWindow.unit === 'MINUTES') {
displayText += 'min';
}
else if(reportWindow.unit === 'HOURS') {
displayText += 'hr';
}
else if(reportWindow.unit === 'SECONDS') {
displayText += 'sec';
pluralSuffix = '';
}
if(parseInt(reportWindow.duration) !== 1) {
displayText += pluralSuffix;
}
return displayText;
}
function toMetric(configuration) {
var thresholdByName = {};
var thresholdNames = [];
for(var i = 0; i < configuration.metrics.length; i++) {
thresholdByName[configuration.metrics[i].name] = parseInt(configuration.metrics[i].value);
thresholdNames.push(configuration.metrics[i].name);
}
return {
metricName: configuration.metricKey,
normalisedName: configuration.metricKey.replace(/\./g, '_'),
metricThresholds: configuration.metrics,
thresholdByName: thresholdByName,
thresholdNames: thresholdNames,
getDisplayName: function() {
return this.metricName;
},
getNormalisedName: function() {
return this.normalisedName;
},
getMetricThresholds: function() {
return this.metricThresholds;
},
getThresholdValue: function(metricName) {
return this.thresholdByName[metricName];
},
getThresholdNames: function() {
return this.thresholdNames;
}
}
}
function createView() {
return {
layout: function(metrics, layoutProperties, reportWindows) {
for(var i = 0; i < metrics.length; i++) {
var container = d3.selectAll('.container').append('div').attr('class', 'metricContainer').
classed('col-md-5', true);
var table = container.append('table').attr('class', 'metricTable');
var metric = metrics[i];
var displayName = metric.getDisplayName();
var normalisedName = metric.getNormalisedName();
if(displayPercentileCharts) {
container.append('div').append('canvas').attr('id', 'percentile_chart_' + normalisedName)
}
table.append('th').
attr('colspan', layoutProperties.columnCount + 1).
text(displayName);
var row = table.append('tr').attr('class', 'row_' + normalisedName);
row.append('td').text('');
for(var j = 0; j < metric.getMetricThresholds().length; j++) {
var threshold = metric.getMetricThresholds()[j];
row.append('td').attr('class', 'thresholdHeading threshold_' + normalisedName + '_' + threshold.name).
text(METRIC_NAME_TO_DISPLAY_NAME[threshold.name]);
}
row.append('td').attr('class', 'thresholdHeading threshold_' + normalisedName + '_COUNT').text('Count');
for(var k = 0; k < reportWindows.length; k++) {
var reportRow = table.append('tr').attr('class', '');
reportRow.append('td').
attr('class', 'reportWindowLabel label_' + normalisedName + '_' + reportWindows[k].duration + '_' + reportWindows[k].unit).
text(windowDurationToDisplayText(reportWindows[k]));
for(var m = 0; m < metric.getMetricThresholds().length; m++) {
var threshold = metric.getMetricThresholds()[m];
var valueCellId = 'value_' +
reportWindows[k].duration + '_' + reportWindows[k].unit + '_' +
normalisedName + '_' + threshold.name;
reportRow.append('td').
attr('class', valueCellId).
text('-');
}
reportRow.append('td').
attr('class', 'value_' +
reportWindows[k].duration + '_' + reportWindows[k].unit + '_' +
normalisedName + '_count').text('-');
}
}
}
}
}
function loadLoop() {
if(d3 && d3.json && d3.selectAll && typeof fluteUtil !== 'undefined' && typeof chartFunctions !== 'undefined') {
flute_init();
} else {
setTimeout(loadLoop, 50);
}
}
loadLoop();
})();