/** * logging */ define([ 'jquery', 'app/init', 'app/util', 'bootbox' ], function($, Init, Util, bootbox) { 'use strict'; var logData = []; // cache object for all log entries var logDataTable = null; // "Datatables" Object // Morris charts data var maxGraphDataCount = 30; // max date entries for a graph var chartData = {}; // chart Data object for all Morris Log graphs var config = { dialogDynamicAreaClass: 'pf-dynamic-area', // class for dynamic areas logGraphClass: 'pf-log-graph' // class for all log Morris graphs }; /** * get log time string * @returns {string} */ var getLogTime = function(){ var serverTime = Util.getServerTime(); var logTime = serverTime.toLocaleTimeString('en-US', { hour12: false }); return logTime; }; /** * shows the logging dialog */ var showDialog = function(){ // dialog content var content = $('
'); // content row for log graphs var rowElementGraphs = $('
', { class: 'row' }); content.append(rowElementGraphs); var tableHeadline = $('

', { text: ' Processes' }).prepend( $('', { class: ['fa', 'fa-fw', 'fa-lg', 'fa-list-alt'].join(' ') })); // add content Structure to dome before table initialization content.append(tableHeadline); // log table area -------------------------------------------------- var logTableArea = $('
', { class: config.dialogDynamicAreaClass }); var logTable = $('', { class: ['compact', 'stripe', 'order-column', 'row-border'].join(' ') }); logTableArea.append(logTable); content.append(logTableArea); // init log table logDataTable = logTable.DataTable( { paging: true, ordering: true, order: [ 1, 'desc' ], autoWidth: false, hover: false, pageLength: 15, lengthMenu: [[10, 15, 25, 50, 50], [10, 15, 25, 50, 50]], data: logData, // load cached logs (if available) language: { emptyTable: 'No entries', zeroRecords: 'No entries found', lengthMenu: 'Show _MENU_ entries', info: 'Showing _START_ to _END_ of _TOTAL_ entries' }, columnDefs: [ { targets: 0, title: '', width: '18px', searchable: false, class: ['text-center'].join(' '), data: 'status' },{ targets: 1, title: '  ', width: '50px', searchable: true, class: 'text-right', data: 'time' },{ targets: 2, title: '  ', width: '35px', searchable: false, class: 'text-right', sType: 'html', data: 'duration' },{ targets: 3, title: 'description', searchable: true, data: 'description' },{ targets: 4, title: 'type', width: '40px', searchable: true, class: ['text-center'].join(' '), data: 'type' },{ targets: 5, title: 'Prozess-ID   ', width: '80px', searchable: false, class: 'text-right', data: 'key' } ] }); // open dialog var logDialog = bootbox.dialog({ title: 'Task-Manager', message: content, size: 'large', buttons: { close: { label: 'close', className: 'btn-default' } } }); // modal dialog is shown logDialog.on('shown.bs.modal', function(e) { // show Morris graphs ---------------------------------------------------------- // function for chart label formation var labelYFormat = function(y){ return Math.round(y) + 'ms'; }; for(var key in chartData) { if(chartData.hasOwnProperty(key)) { // create a chart for each key var colElementGraph = $('
', { class: ['col-md-6'].join(' ') }); // graph element var graphElement = $('
', { class: config.logGraphClass }); var graphArea = $('
', { class: config.dialogDynamicAreaClass }).append( graphElement ); // headline var headline = $('

', { text: key }).prepend( $('', { class: ['txt-color', 'txt-color-grayLight'].join(' '), text: 'Prozess-ID: ' }) ); // show update ping between function calls var updateElement = $('', { class: ['txt-color', 'txt-color-blue', 'pull-right'].join(' ') }); headline.append(updateElement).append('
'); // show average execution time var averageElement = $('', { class: 'pull-right' }); headline.append(averageElement); colElementGraph.append( headline ); colElementGraph.append( graphArea ); graphArea.showLoadingAnimation(); rowElementGraphs.append( colElementGraph ); // cache DOM Elements that will be updated frequently chartData[key].averageElement = averageElement; chartData[key].updateElement = updateElement; chartData[key].graph = Morris.Area({ element: graphElement, data: [], xkey: 'x', ykeys: ['y'], labels: [key], units: 'ms', parseTime: false, ymin: 0, yLabelFormat: labelYFormat, padding: 10, hideHover: true, pointSize: 3, lineColors: ['#375959'], pointFillColors: ['#477372'], pointStrokeColors: ['#313335'], lineWidth: 2, grid: false, gridStrokeWidth: 0.3, gridTextSize: 9, gridTextFamily: 'Oxygen Bold', gridTextColor: '#63676a', behaveLikeLine: true, goals: [], goalLineColors: ['#66c84f'], smooth: false, fillOpacity: 0.3, resize: true }); graphArea.hideLoadingAnimation(); } } // ------------------------------------------------------------------------------ // add TableTool Buttons var tt = new $.fn.DataTable.TableTools( logDataTable, { sSwfPath: require.toUrl('') + 'lib/datatables/extensions/tabletools/swf/copy_csv_xls.swf', aButtons: [ 'copy', 'csv', 'print' ] }); $(tt.fnContainer()).insertBefore('.bootbox-body div.dataTables_wrapper'); // add button icons $('.DTTT_button_csv').prepend( $('', { class: ['fa', 'fa-fw', 'fa-download'].join(' ') })); $('.DTTT_button_copy').prepend( $('', { class: ['fa', 'fa-fw', 'fa-clipboard'].join(' ') })); $('.DTTT_button_print').prepend( $('', { class: ['fa', 'fa-fw', 'fa-print'].join(' ') })); }); // modal dialog is closed logDialog.on('hidden.bs.modal', function(e) { // clear memory -> destroy all charts for (var key in chartData) { if (chartData.hasOwnProperty(key)) { chartData[key].graph = null; } } }); // modal dialog before hide logDialog.on('hide.bs.modal', function(e) { // destroy logTable logDataTable.destroy(true); logDataTable= null; // remove event -> prevent calling this multiple times $(this).off('hide.bs.modal'); }); }; /** * updates the log graph for a log key * @param key * @param duration */ var updateLogGraph = function(key, duration){ // check if graph data already exist if( !(chartData.hasOwnProperty(key))){ chartData[key] = {}; chartData[key].data = []; chartData[key].graph = null; chartData[key].averageElement = null; chartData[key].updateElement = null; } // add new value chartData[key].data.unshift(duration); if(chartData[key].data.length > maxGraphDataCount){ chartData[key].data = chartData[key].data.slice(0, maxGraphDataCount); } function getGraphData(data) { var tempChartData = { data: [], dataSum: 0, average: 0 }; for(var x = 0; x < maxGraphDataCount; x++){ var value = 0; if(data[x]){ value = data[x]; tempChartData.dataSum = Number( (tempChartData.dataSum + value).toFixed(2) ); } tempChartData.data.push({ x: x, y: value }); } // calculate average tempChartData.average = Number( ( tempChartData.dataSum / data.length ).toFixed(2) ); return tempChartData; } var tempChartData = getGraphData(chartData[key].data); // add new data to graph (Morris chart) - if is already initialized if(chartData[key].graph !== null){ var avgElement = chartData[key].averageElement; var updateElement = chartData[key].updateElement; var delay = Util.getCurrentTriggerDelay( key, 0 ); if(delay){ updateElement[0].textContent = ' delay: ' + delay + 'ms '; } // set/change average line chartData[key].graph.options.goals = [tempChartData.average]; // change avg. display avgElement[0].textContent = 'Avg. ' + tempChartData.average + 'ms'; var avgStatus = getLogStatusByDuration(key, tempChartData.average); var avgStatusClass = Util.getLogInfo( avgStatus, 'class' ); //change avg. display class if( !avgElement.hasClass(avgStatusClass) ){ // avg status changed! avgElement.removeClass().addClass('pull-right txt-color ' + avgStatusClass); // change goals line color if(avgStatus === 'warning'){ chartData[key].graph.options.goalLineColors = ['#e28a0d']; $(document).setProgramStatus('problem'); }else{ chartData[key].graph.options.goalLineColors = ['#5cb85c']; } } // set new data and redraw chartData[key].graph.setData( tempChartData.data ); } return tempChartData.data; }; /** * get the log "status" by log duration (ms). * If duration > warning limit -> show as warning * @param logKey * @param logDuration * @returns {string} */ var getLogStatusByDuration = function(logKey, logDuration){ var logStatus = 'info'; if( logDuration > Init.timer[logKey].EXECUTION_LIMIT ){ logStatus = 'warning'; } return logStatus; }; /** * get the css class for a specific log type * @param logType * @returns {string} */ var getLogTypeIconClass = function(logType){ var logIconClass = ''; switch(logType){ case 'client': logIconClass = 'fa-user'; break; case 'server': logIconClass = 'fa-download'; break; } return logIconClass; }; /** * init logging -> set global log event */ var init = function(){ var maxEntries = 150; // set global logging listener $(window).on('pf:log', function(e, logKey, options){ // check required logging information if( options && options.duration && options.description ){ var logDescription = options.description; var logDuration = options.duration; var logType = options.type; // check log status by duration var logStatus = getLogStatusByDuration(logKey, logDuration); var statusClass = Util.getLogInfo( logStatus, 'class' ); var typeIconClass = getLogTypeIconClass(logType); // update graph data updateLogGraph(logKey, logDuration); var logRowData = { status: '', time: getLogTime(), duration: '' + logDuration + 'ms', description: logDescription, type: '', key: logKey }; if(logDataTable){ // add row if dataTable is initialized before new log logDataTable.row.add( logRowData ).draw(false); }else{ // add row data to cache logData.push(logRowData); } } // delete old log entries from table --------------------------------- var rowCount = logData.length; if( rowCount >= maxEntries ){ if(logDataTable){ logDataTable.rows(0, {order:'index'}).remove().draw(false); }else{ logData.shift(); } } // cache logs in order to keep previous logs in table after reopening the dialog if(logDataTable){ logData = logDataTable.rows({order:'index'}).data(); } }); }; return { init: init, getLogTime: getLogTime, showDialog: showDialog }; });