blob: 5ec9b95100131c8a4a5396fab855f4169324ff9a [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.
*/
(function() {
'use strict';
var featureControllers = angular.module('featureControllers');
var feature = featureControllers.register("metrics");
// ==============================================================
// = Initialization =
// ==============================================================
// ==============================================================
// = Controller =
// ==============================================================
// ========================= Dashboard ==========================
feature.navItem("dashboard", "Metrics", "line-chart");
feature.controller('dashboard', function(PageConfig, $scope, $http, $q, UI, Site, Authorization, Application, Entities) {
var _siteApp = Site.currentSiteApplication();
var _druidConfig = _siteApp.configObj.druid;
var _refreshInterval;
var _menu_newChart;
$scope.lock = false;
$scope.dataSourceListReady = false;
$scope.dataSourceList = [];
$scope.dashboard = {
groups: []
};
$scope.dashboardEntity = null;
$scope.dashboardReady = false;
$scope._newMetricFilter = "";
$scope._newMetricDataSrc = null;
$scope._newMetricDataMetric = null;
$scope.tabHolder = {};
$scope.endTime = app.time.now();
$scope.startTime = $scope.endTime.clone();
// =================== Initialization ===================
if(!_druidConfig || !_druidConfig.coordinator || !_druidConfig.broker) {
$.dialog({
title: "OPS",
content: "Druid configuration can't be empty!"
});
return;
}
$scope.autoRefreshList = [
{title: "Last 1 Month", timeDes: "day", getStartTime: function(endTime) {return endTime.clone().subtract(1, "month");}},
{title: "Last 1 Day", timeDes: "thirty_minute", getStartTime: function(endTime) {return endTime.clone().subtract(1, "day");}},
{title: "Last 6 Hour", timeDes: "fifteen_minute", getStartTime: function(endTime) {return endTime.clone().subtract(6, "hour");}},
{title: "Last 2 Hour", timeDes: "fifteen_minute", getStartTime: function(endTime) {return endTime.clone().subtract(2, "hour");}},
{title: "Last 1 Hour", timeDes: "minute", getStartTime: function(endTime) {return endTime.clone().subtract(1, "hour");}}
];
$scope.autoRefreshSelect = $scope.autoRefreshList[2];
// ====================== Function ======================
$scope.setAuthRefresh = function(item) {
$scope.autoRefreshSelect = item;
$scope.chartRefresh(true);
};
$scope.refreshTimeDisplay = function() {
PageConfig.pageSubTitle = common.format.date($scope.startTime) + " ~ " + common.format.date($scope.endTime) + " [refresh interval: 30s]";
};
$scope.refreshTimeDisplay();
// ======================= Metric =======================
// Fetch metric data
$http.get(_druidConfig.coordinator + "/druid/coordinator/v1/metadata/datasources", {withCredentials: false}).then(function(data) {
var _endTime = new moment();
var _startTime = _endTime.clone().subtract(1, "day");
var _intervals = _startTime.toISOString() + "/" + _endTime.toISOString();
$scope.dataSourceList = $.map(data.data, function(dataSrc) {
return {
dataSource: dataSrc,
metricList: []
};
});
// List dataSource metrics
var _metrixList_promiseList = $.map($scope.dataSourceList, function(dataSrc) {
var _data = JSON.stringify({
"queryType": "groupBy",
"dataSource": dataSrc.dataSource,
"granularity": "all",
"dimensions": ["metric"],
"aggregations": [
{
"type":"count",
"name":"count"
}
],
"intervals": [_intervals]
});
return $http.post(_druidConfig.broker + "/druid/v2", _data, {withCredentials: false}).then(function(response) {
dataSrc.metricList = $.map(response.data, function(entity) {
return entity.event.metric;
});
});
});
$q.all(_metrixList_promiseList).finally(function() {
$scope.dataSourceListReady = true;
$scope._newMetricDataSrc = $scope.dataSourceList[0];
$scope._newMetricDataMetric = common.getValueByPath($scope._newMetricDataSrc, "metricList.0");
});
}, function() {
$.dialog({
title: "OPS",
content: "Fetch data source failed. Please check Site Application Metrics configuration."
});
});
// Filter data source
$scope.dataSourceMetricList = function(dataSrc, filter) {
filter = (filter || "").toLowerCase().trim().split(/\s+/);
return $.grep((dataSrc && dataSrc.metricList) || [], function(metric) {
for(var i = 0 ; i < filter.length ; i += 1) {
if(metric.toLowerCase().indexOf(filter[i]) === -1) return false;
}
return true;
});
};
// New metric select
$scope.newMetricSelectDataSource = function(dataSrc) {
if(dataSrc !== $scope._newMetricDataMetric) $scope._newMetricDataMetric = dataSrc.metricList[0];
$scope._newMetricDataSrc = dataSrc;
};
$scope.newMetricSelectMetric = function(metric) {
$scope._newMetricDataMetric = metric;
};
// Confirm new metric
$scope.confirmSelectMetric = function() {
var group = $scope.tabHolder.selectedPane.data;
$("#metricMDL").modal('hide');
group.charts.push({
chart: "line",
dataSource: $scope._newMetricDataSrc.dataSource,
metric: $scope._newMetricDataMetric,
aggregations: ["max"]
});
$scope.chartRefresh();
};
// ======================== Menu ========================
$scope.newGroup = function() {
if($scope.lock) return;
UI.createConfirm("Group", {}, [{field: "name"}], function(entity) {
if(common.array.find(entity.name, $scope.dashboard.groups, "name")) {
return "Group name conflict";
}
}).then(null, null, function(holder) {
$scope.dashboard.groups.push({
name: holder.entity.name,
charts: []
});
holder.closeFunc();
setTimeout(function() {
$scope.tabHolder.setSelect(holder.entity.name);
}, 0);
});
}
function deleteGroup() {
var group = $scope.tabHolder.selectedPane.data;
UI.deleteConfirm(group.name).then(null, null, function(holder) {
common.array.remove(group, $scope.dashboard.groups);
holder.closeFunc();
});
}
_menu_newChart = {title: "Add Metric", func: function() {$scope.newChart();}};
Object.defineProperties(_menu_newChart, {
icon: {
get: function() {return $scope.dataSourceListReady ? 'plus' : 'refresh fa-spin';}
},
disabled: {
get: function() {return !$scope.dataSourceListReady;}
}
});
$scope.menu = Authorization.isRole('ROLE_ADMIN') ? [
{icon: "cog", title: "Configuration", list: [
_menu_newChart,
{icon: "trash", title: "Delete Group", danger: true, func: deleteGroup}
]},
{icon: "plus", title: "New Group", func: $scope.newGroup}
] : [];
// ===================== Dashboard ======================
$scope.dashboardList = Entities.queryEntities("GenericResourceService", {
site: Site.current().tags.site,
application: Application.current().tags.application
});
$scope.dashboardList._promise.then(function(list) {
$scope.dashboardEntity = list[0];
$scope.dashboard = $scope.dashboardEntity ? common.parseJSON($scope.dashboardEntity.value) : {groups: []};
$scope.chartRefresh();
}).finally(function() {
$scope.dashboardReady = true;
});
$scope.saveDashboard = function() {
$scope.lock = true;
if(!$scope.dashboardEntity) {
$scope.dashboardEntity = {
tags: {
site: Site.current().tags.site,
application: Application.current().tags.application,
name: "/metric_dashboard/dashboard/default"
}
};
}
$scope.dashboardEntity.value = common.stringify($scope.dashboard);
Entities.updateEntity("GenericResourceService", $scope.dashboardEntity)._promise.then(function() {
$.dialog({
title: "Done",
content: "Save success!"
});
}, function() {
$.dialog({
title: "POS",
content: "Save failed. Please retry."
});
}).finally(function() {
$scope.lock = false;
});
};
// ======================= Chart ========================
$scope.configTargetChart = null;
$scope.configPreviewChart = null;
$scope.chartConfig = {
xType: "time"
};
$scope.chartTypeList = [
{icon: "line-chart", chart: "line"},
{icon: "area-chart", chart: "area"},
{icon: "bar-chart", chart: "column"},
{icon: "pie-chart", chart: "pie"}
];
$scope.chartSeriesList = [
{name: "Min", series: "min"},
{name: "Max", series: "max"}
];
$scope.newChart = function() {
$("#metricMDL").modal();
};
$scope.configPreviewChartMinimumCheck = function() {
$scope.configPreviewChart.min = $scope.configPreviewChart.min === 0 ? undefined : 0;
window.ccc1 = $scope.getChartConfig($scope.configPreviewChart);
};
$scope.seriesChecked = function(chart, series) {
if(!chart) return false;
return $.inArray(series, chart.aggregations || []) !== -1;
};
$scope.seriesCheckClick = function(chart, series) {
if(!chart) return;
if($scope.seriesChecked(chart, series)) {
common.array.remove(series, chart.aggregations);
} else {
chart.aggregations.push(series);
}
$scope.chartSeriesUpdate(chart);
};
$scope.chartSeriesUpdate = function(chart) {
chart._data = $.map(chart._oriData, function(series) {
if($.inArray(series.key, chart.aggregations) !== -1) return series;
});
};
$scope.getChartConfig = function(chart) {
if(!chart) return null;
var _config = chart._config = chart._config || $.extend({}, $scope.chartConfig);
_config.yMin = chart.min;
return _config;
};
$scope.configChart = function(chart) {
$scope.configTargetChart = chart;
$scope.configPreviewChart = $.extend({}, chart);
delete $scope.configPreviewChart._config;
$("#chartMDL").modal();
setTimeout(function() {
$(window).resize();
}, 200);
};
$scope.confirmUpdateChart = function() {
$("#chartMDL").modal('hide');
common.extend($scope.configTargetChart, $scope.configPreviewChart);
$scope.chartSeriesUpdate($scope.configTargetChart);
if($scope.configTargetChart._holder) $scope.configTargetChart._holder.refreshAll();
};
$scope.deleteChart = function(group, chart) {
UI.deleteConfirm(chart.metric).then(null, null, function(holder) {
common.array.remove(chart, group.charts);
holder.closeFunc();
$scope.chartRefresh();
});
};
$scope.chartRefresh = function(forceRefresh) {
setTimeout(function() {
$scope.endTime = app.time.now();
$scope.startTime = $scope.autoRefreshSelect.getStartTime($scope.endTime);
var _intervals = $scope.startTime.toISOString() + "/" + $scope.endTime.toISOString();
$scope.refreshTimeDisplay();
$.each($scope.dashboard.groups, function (i, group) {
$.each(group.charts, function (j, chart) {
var _data = JSON.stringify({
"queryType": "groupBy",
"dataSource": chart.dataSource,
"granularity": $scope.autoRefreshSelect.timeDes,
"dimensions": ["metric"],
"filter": {"type": "selector", "dimension": "metric", "value": chart.metric},
"aggregations": [
{
"type": "max",
"name": "max",
"fieldName": "maxValue"
},
{
"type": "min",
"name": "min",
"fieldName": "maxValue"
}
],
"intervals": [_intervals]
});
if (!chart._data || forceRefresh) {
$http.post(_druidConfig.broker + "/druid/v2", _data, {withCredentials: false}).then(function (response) {
chart._oriData = nvd3.convert.druid([response.data]);
$scope.chartSeriesUpdate(chart);
if(chart._holder) chart._holder.refresh();
});
} else {
if(chart._holder) chart._holder.refresh();
}
});
});
}, 0);
};
_refreshInterval = setInterval(function() {
if(!$scope.dashboardReady) return;
$scope.chartRefresh(true);
}, 1000 * 30);
// ====================== Clean Up ======================
$scope.$on('$destroy', function() {
clearInterval(_refreshInterval);
});
});
})();