sentilo-catalog-web/src/main/webapp/static/js/sentilo/metrics.js
/**
* Dashboard functions
*/
//config
var maxPointsToShowIntoChart = 30;
//order metrics
var orderMap = new Map();
orderMap.set("cpu",0);
orderMap.set("system_memory",1);
orderMap.set("process_memory",4);
orderMap.set("file_systems_0",3);
orderMap.set("file_systems_1",3);
orderMap.set("file_systems_2",3);
orderMap.set("threads",2);
orderMap.set("events",5);
orderMap.set("data_sources",6);
orderMap.set("thread_pool",7);
orderMap.set("requests",1000);
//color charts defined
var chartColors = {
red: 'rgb(245, 80, 86)',
orange: 'rgb(255, 159, 64)',
yellow: 'rgb(255, 205, 86)',
green: 'rgb(75, 192, 192)',
blue: 'rgb(71, 171,241)',
purple: 'rgb(136, 51, 170)',
grey: 'rgb(251, 251, 251)'
};
//config metrics to convert
var unityConversor = ["file_systems","file_systems_0",
"file_systems_1","system_memory","process_memory"];
//chart types per metric
var charts=["cpu", "system_memory", "threads"]
var chartNames=[];
//global variables
const diffMilisecons=120000;
var isReadableByte=false;
var progressBar;
var metrics=[];
var metricsOnRefresh=[];
var collectionCanvas=[];
var collectionCanvasOnRefresh=[];
var collectionChartTimes= new Map();
//load page
var main=function(data){
collectionChartTimes=chartTimes(data);
metrics=metricsName(data);
$.each(data.artifactsMetrics, function(index, element ) {
var name = element.name.split(":");
var tabname= name[1] + "-" + name[2];
var state=getStatus(element.timestamp,element.state);
buildAbstractElement(name[1],name[2], element.timestamp, state ) ;
buildTabContent(name[1],name[2]);
buildCanvas(name[1],name[2],element.name,timeToDate(element.timestamp));
buildSingleObjects(element.metrics,tabname);
});
collectionCanvas = collectionCanvasOnRefresh;
activeTab();
buildConfigChartsByMetrics();
getData();
reorder();
}
var bindData=function(data){
//chart times
collectionChartTimes=chartTimes(data);
//get new metrics
metricsOnRefresh=metricsName(data);
//clean abstract and metrics elements to refresh. Keep charts
var activeMetric=$('#abstract').find('li.active').attr('id');
cleanPage();
collectionCanvasOnRefresh=[];
$.each(data.artifactsMetrics, function(index, element ) {
var name = element.name.split(":");
var tabname= name[1] + "-" + name[2];
var state=getStatus(element.timestamp,element.state);
buildAbstractElement(name[1],name[2], element.timestamp, state ) ;
buildTabContent(name[1],name[2]);
buildCanvas(name[1],name[2],element.name,timeToDate(element.timestamp));
buildSingleObjects(element.metrics,tabname);
});
collectionCanvas = collectionCanvasOnRefresh;
buildConfigChartsByMetrics();
getData();
activeTabAfterRemove(activeMetric);
reorder();
}
var chartTimes=function(data){
var d = new Map()
$.each(data.artifactsMetrics, function(index, element ) {
var artefact = element.name.split(":");
var name= artefact[1] + "-" + artefact[2];
d.set(name,element.timestamp);
});
return d;
}
var metricsName=function(data){
var metrics=[];
$.each(data.artifactsMetrics, function(index, element ) {
var name = element.name.split(":");
var tabname= name[1] + "-" + name[2];
metrics.push(tabname);
});
return metrics;
}
var buildSingleObjects=function(obj, tabname, k){
Object.entries(obj).forEach(entry => {
let key = entry[0];
let value = entry[1];
if(typeof value == 'object'){
if(key=='requests' && value.length == undefined){
$(jqEscape(tabname+"content")+" ul").last().append(
buildAgroupper(tabname,key,key));
buildSimpleObjects(value,tabname, key, false);
}else if (key=='requests' && value.length > 0){
Object.entries(value).forEach(entry => {
let k = entry[0];
let v = entry[1];
$(jqEscape(tabname+"content")+" ul").last().append(
buildAgroupper(tabname,key+"_"+k,key));
buildSimpleObjects(v,tabname, key+"_"+k,false);
});
}else if(key=='process_memory'){
$(jqEscape(tabname+"content")+" ul").last().append(
buildAgroupper(tabname,key,key));
buildSimpleObjects(value,tabname, key, true);
}else if(value.length>0){
Object.entries(value).forEach(entry => {
let k = entry[0];
let v = entry[1];
$(jqEscape(tabname+"content")+" ul").last().append(
buildAgroupper(tabname,key +"_"+k,key));
$("[id^='"+ tabname +"'][id$='Stats']").last().append(
buildProgressBar(v));
buildSimpleObjects(v,tabname, key+"_"+k, false);
});
}else{
$(jqEscape(tabname+"content")+" ul").last().append(
buildAgroupper(tabname,key,key));
$(jqEscape(tabname+"_"+key+"Stats")).last().append(
buildProgressBar(value));
isReadableByte=isReadableByteFunction(key);
buildSingleObjects(value,tabname, key)
}
}else{
$(jqEscape(tabname+"_"+k +"Stats")).last().append(
buildElement(tabname,value,key,'',isReadableByte));
}
});
}
var buildSimpleObjects=function(obj, tabname, key, showProgressBar){
Object.entries(obj).forEach(entry => {
let k = entry[0];
let v = entry[1];
if(typeof v == 'object'){
$(jqEscape(tabname+"_"+key+"Stats")).last().append(
buildSubAgroupper(tabname,key,k));
if(showProgressBar){
$("[id^='"+ tabname +"'][id$='Stats']").last().append(
buildProgressBar(v));
}
buildSimpleObjects(v,tabname,key);
}else{
isReadableByte=isReadableByteFunction(key);
$("[id^='"+ tabname +"'][id$='Stats']").last(".agrouper").append(
buildElement(tabname,v,k,'',isReadableByte));
}
});
}
var cleanPage=function(){
$("#abstract ul li").remove();
$(".tabcontent [id$='_element'] li").remove();
for(var m in metrics){
if(!metricsOnRefresh.includes(metrics[m])){
$(jqEscape(metrics[m]+"content")).remove();
}
}
metrics=metricsOnRefresh;
metricsOnRefresh=[];
}
var activeTabAfterRemove=function(activeMetric){
if($(jqEscape(activeMetric)).length==0){
activeTab();
}else{
$(jqEscape(activeMetric)).addClass('active');
$('#metrics-name small').text($('#abstract li.active').attr('id'));
$('.tabcontent').hide();
$(jqEscape(activeMetric+'content')).show();
}
}
var buildAbstractElement= function (host, artefact , lastTime, status){
var element = '<li id="'+host+"-"+artefact+'">';
element +='<div class="accordion" id="activityAccordion'+host+"-"+artefact+'">';
element +='<div class="accordion-group">';
element +='<div class="accordion-heading">';
element +='<a class="accordion-toggle" data-toggle="collapse" data-parent="#activityAccordion'+host+"-"+artefact+'"';
element +='href="#activityAccordionCollapse'+host+"-"+artefact+'"> <i class="icon-signal"></i> '+artefact;
element +='<i class="icon-chevron-down pull-right"></i> </a>';
element +='</div>';
element +='<div id="activityAccordionCollapse'+artefact+'" class="accordion-body collapse in">';
element +='<div class="accordion-inner metrics">';
element +='<span>'+host+'</span><br><span>'+getStatusString(status)+'</span><br><span class="infobox-footer">'+ timeToDate(lastTime) +'</span><br>';
element +='</div>';
element +='</div>';
element +='</div>';
element +='</div>';
element +='</div></li>';
$("#abstract ul").append(element);
}
var buildCanvas=function(host, artefact, metricname,time){
var name = host +"-"+ artefact;
var element="";
for(var chart in charts){
if($(jqEscape(name+"_chart_"+charts[chart])).length==0){
element +='<div class="accordion accustom" id="activityAccordion'+name+"_chart_"+charts[chart]+'">';
element +='<div class="accordion-group">';
element +='<div class="accordion-heading">';
element +='<a class="accordion-toggle" data-toggle="collapse" data-parent="#activityAccordion'+name+"_chart_"+charts[chart]+'"';
element +='href="#activityAccordionCollapse'+name+"_chart_"+charts[chart]+'"> <i class="icon-signal"></i> '+charts[chart];
element +='<i class="icon-chevron-down pull-right"></i> </a>';
element +='</div>';
element +='<div id="activityAccordionCollapse'+name+"_chart_"+charts[chart]+'" class="accordion-body collapse in">';
element +='<div class="accordion-inner metrics">';
element +='<div id="placeholder" class="ct-chart-centered-labels">';
element +='<div id="'+name+"_chart_"+charts[chart]+'" data-parent="'+ name + "_" +charts[chart]+'" class="chart" style="height:216px;margin-right:25px;background-color: #fbfbfb;margin-bottom:50px;margin-top:25px"></div>';
element +='</div>';
element +='</div>';
element +='</div>';
element +='</div>';
element +='</div>';
if(!collectionCanvasOnRefresh.includes(name+"_chart_"+charts[chart]) && charts[chart] != undefined ){
collectionCanvasOnRefresh.push(name+"_chart_"+charts[chart]);
}
}
}
$(jqEscape(name+"_chart")).append(element);
}
var refreshChartsTimes=function(host, artefact, metricname,time){
var name = host +"-"+ artefact;
collectionChartTimes.set(name, time);
}
var buildTabContent=function(host, artefact){
var name = host +"-"+ artefact;
var element="";
if(($(jqEscape(name+"content")).length==0)){
element +="<div id='"+name+"content' class='tabcontent'>";
element +="<div id='"+name+"_chart'></div>";
element +="<ul id='"+name+"_element'></ul></div>";
element +="</div>";
$("#container").append(element);
}
}
var activeTab=function(){
if($('#abstract li').hasClass('active')){
return;
}else{
$('#abstract li:first').addClass('active');
$('#metrics-name small').text($('#abstract li.active').attr('id'));
$('.tabcontent').hide();
$('.tabcontent:first').show();
}
}
var buildAgroupper=function(tabname,key,label){
var id=tabname+"_"+key;
var element='';
element+="<li data-node='" + order(key)+ "'>"
element+="<div class=''>";
element+="<div class='accordion' id='"+id+"'>";
element+="<div class='accordion-group'>";
element+="<div class='accordion-heading'>";
element+="<span>";
element+="<a class='accordion-toggle' data-toggle='collapse' data-parent='#"+id+"Accordion' href='#"+id+"AccordionCollapse'> <i class='icon-map-marker'></i>"+label;
element+="<i class='icon-chevron-down pull-right'></i> </a>";
element+="<div id='"+id+"AccordionCollapse' class='accordion-body in collapse' style='height: auto;'>";
element+="<div class='accordion-inner metrics' id='"+id+"Stats'>";
element+="</div>";
element+="</div>";
element+="</div>";
element+="</div>";
element+="</div>";
element+="</li>";
return element;
}
var buildSubAgroupper=function(tabname,parentDiv,property){
var id=tabname+"-"+parentDiv+"_"+property;
var element='';
element+="<div id='"+id+"Stats' class=''><div class='agrouper'><strong>"+property+"</strong></div>";
element+="<div id='"+id+"progressBar' class=''><div class='agrouper'></div>";
return element;
}
var buildElement=function(tabname,obj, property,parent,isReadableByte){
var element='';
element+="<div id='"+property+"_stats' class=''>";
if(isReadableByte){
element +="<span class='key'>"+property+"</span><span class='value'>"+readableBytes(obj)+"</span>";
}else{
element +="<span class='key'>"+property+"</span><span class='value'>"+obj+"</span>";
}
element+="</div>";
return element;
}
var buildProgressBar=function(obj){
var element='';
var p=percent(obj);
if(p>0){
element += '<div class="progress">';
element += '<div class="bar ' + getStyleBar(p) + '" style="width:'+p+'%" aria-valuenow="25" aria-valuemin="0" aria-valuemax="100">'+p+'%</div>';
element +='</div>'
}else{
element += '<div class="progress emptyprogress"></div>' ;
}
return element;
}
var getStatus=function(t, s){
var diff = compareDates(t);
if(diff>diffMilisecons){
return "RED";
}else{
return s;
}
}
var getStatusString=function(status){
if(status=="GREEN"){
return '<span class="indicator label-success"></span>running';
}
else if(status=="ORANGE"){
return '<span class="indicator label-warning"></span>running';
}else{
return '<span class="indicator label-danger"></span>stopped';
}
}
var percent=function(obj){
var res;
if(obj <= 0) return obj;
if(obj.hasOwnProperty('free') && obj.hasOwnProperty('total')){
if(obj.free==0)return 0;
res= parseFloat(parseInt(obj.free, 10) * 100)/ parseInt(obj.total, 10);
if(res<=0)return 0;
return (Math.round(100 - res))
}
if(obj.hasOwnProperty('used') && obj.hasOwnProperty('max')){
if(obj.used==0)return 0;
res= parseFloat(parseInt(obj.used, 10) * 100)/ parseInt(obj.max, 10);
if(res<=0)return 0;
return (Math.round(res))
}
}
var getStyleBar=function (obj){
if(obj < 61){
return "bar-success";
}else if(obj < 90){
return "bar-warning";
}else if (obj >= 90){
return "bar-danger";
}
}
var isReadableByteFunction=function(property){
if(unityConversor.includes(property)){
return true;
}
return false;
}
var readableBytes=function (bytes) {
if(Number.isInteger(bytes) && bytes>1024){
var i = Math.floor(Math.log(bytes) / Math.log(1024)),
sizes = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
return (bytes / Math.pow(1024, i)).toFixed(2) * 1 + ' ' + sizes[i];
}else{
return bytes;
}
}
var compareDates=function(date){
var now= $.now();
var res = now - date;
return res;
}
var timeToDate=function(time){
var date = new Date(time);
var formatDate = date.getFullYear() + '-' +('0' + (date.getMonth()+1)).slice(-2)+ '-' + date.getDate() +
' '+date.getHours()+ ':'+('0' + (date.getMinutes())).slice(-2)+ ':'+date.getSeconds();
return formatDate.toString();
}
var escapeRegExp=function (str) {
return str.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
}
var order=function(key){
var order=1000;
if(orderMap.get(key) != undefined){
order=orderMap.get(key)
}
return order;
}
var reorder=function(){
var id = $('#abstract').find('li.active').attr('id');
var idContent="#"+id+"_element";
$(idContent).find('li').sort(function (a, b) {
return +a.dataset.node - +b.dataset.node;
}).appendTo( $(idContent) );
}
var buildConfigChartsByMetrics=function(){
for(var c in collectionCanvas){
if(window.collectionCanvas[c]!=undefined){
configChart(collectionCanvas[c]);
}
}
}
var configChart=function(metric){
if(metric.includes('cpu') ){
window.metric= chartMetrics(metric,series(['system_load','process_load']),
['system_load','process_load']);
}else if(metric.includes('system_memory')) {
window.metric=chartMetrics(metric,series(['used','free']),
['used','free']);
}else if(metric.includes('threads')){
window.metric=chartMetrics(metric, series(['total','daemon']),
['total','daemon']);
}else{
return;
}
chartNames.push(window.metric);
}
var series=function(labels){
var obj=[];
for (i = 0; i < labels.length; i++) {
var id = labels[i];
tmp = {
name: id,
data: []
};
obj.push(tmp);
}
return JSON.stringify(obj);
}
var getData = function() {
for (var c in chartNames){
var newTime=addTimeValue(chartNames[c]);
if(newTime != window.chartNames[c].data.labels[window.chartNames[c].data.labels.length -1]){
// Limit data to display to the last 30 points (more points transform chart into something unusable)
if(window.chartNames[c].data.labels.length > maxPointsToShowIntoChart){
window.chartNames[c].data.labels.shift();
window.chartNames[c].data.series.forEach(function(serie) {
serie.data.shift();
});
}
window.chartNames[c].data.labels.push(newTime);
window.chartNames[c].data.series.forEach(function(serie) {
serie.data.push({x:convertTimeToLong(newTime), y:addGlobalValue(chartNames[c], serie)});
});
window.chartNames[c].update(window.chartNames[c].data);
}
}
}
var addGlobalValue=function(chart,serie) {
var parent =chart.container.dataset.parent;
var label = serie.name;
var value = $(jqEscape(parent)+" "+jqEscape(label+'_stats')+" span.value").text().split(" ");
return value[0] ;
}
var addTimeValue = function(chart) {
var parent =chart.container.dataset.parent;
var artefact=parent.split("_");
var value = new Date(collectionChartTimes.get(artefact[0]));
var h = addZero(value.getHours());
var m = addZero(value.getMinutes());
var s = addZero(value.getSeconds());
return h + ":" + m + ":" + s;
}
var addZero = function(i) {
if (i < 10) {
i = "0" + i;
}
return i;
}
function jqEscape(elementId) {
// Due to elementId could contains dots (e.g. dev.sentilo.io-mock-agent), jQuery selectors need to escape meta-characters
// https://api.jquery.com/category/selectors/
// https://learn.jquery.com/using-jquery-core/faq/how-do-i-select-an-element-by-an-id-that-has-characters-used-in-css-notation/
return "#" + elementId.replace( /(:|\.|\[|\]|,|=|@)/g, "\\$1" );
}