')).addClass('float-start'));
}
$('#gridchart' + runtime.chartAI).parent().append($legend);
if (initialize !== true) {
runtime.charts['c' + runtime.chartAI] = chartObj;
buildRequiredDataList();
}
// time span selection
$('#gridchart' + runtime.chartAI).on('jqplotMouseDown', function (ev, gridpos, datapos) {
drawTimeSpan = true;
selectionTimeDiff.push(datapos.xaxis);
if ($('#selection_box').length) {
$('#selection_box').remove();
}
var selectionBox = $('
');
// eslint-disable-next-line compat/compat
$(document.body).append(selectionBox);
selectionStartX = ev.pageX;
selectionStartY = ev.pageY;
selectionBox.attr({
id: 'selection_box'
}).css({
top: selectionStartY - gridpos.y,
left: selectionStartX
}).fadeIn();
});
$('#gridchart' + runtime.chartAI).on('jqplotMouseUp', function (ev, gridpos, datapos) {
if (!drawTimeSpan || editMode) {
return;
}
selectionTimeDiff.push(datapos.xaxis);
if (selectionTimeDiff[1] <= selectionTimeDiff[0]) {
selectionTimeDiff = [];
return;
}
// get date from timestamp
var min = new Date(Math.ceil(selectionTimeDiff[0]));
var max = new Date(Math.ceil(selectionTimeDiff[1]));
getLogAnalyseDialog(min, max);
selectionTimeDiff = [];
drawTimeSpan = false;
});
$('#gridchart' + runtime.chartAI).on('jqplotMouseMove', function (ev) {
if (!drawTimeSpan || editMode) {
return;
}
if (selectionStartX !== undefined) {
$('#selection_box').css({
width: Math.ceil(ev.pageX - selectionStartX)
}).fadeIn();
}
});
$('#gridchart' + runtime.chartAI).on('jqplotMouseLeave', function () {
drawTimeSpan = false;
});
// eslint-disable-next-line compat/compat
$(document.body).on('mouseup', function () {
if ($('#selection_box').length) {
$('#selection_box').remove();
}
});
// Edit, Print icon only in edit mode
$('#chartGrid').find('div svg').find('*[zIndex=20], *[zIndex=21], *[zIndex=19]').toggle(editMode);
runtime.chartAI++;
}
function getLogAnalyseDialog(min, max) {
var $logAnalyseDialog = $('#logAnalyseDialog');
var $dateStart = $logAnalyseDialog.find('input[name="dateStart"]');
var $dateEnd = $logAnalyseDialog.find('input[name="dateEnd"]');
$dateStart.prop('readonly', true);
$dateEnd.prop('readonly', true);
var dlgBtns = {
[Messages.strFromSlowLog]: {
text: Messages.strFromSlowLog,
class: 'btn btn-secondary'
},
[Messages.strFromGeneralLog]: {
text: Messages.strFromGeneralLog,
class: 'btn btn-secondary'
}
};
dlgBtns[Messages.strFromSlowLog].click = function () {
loadLog('slow', min, max);
$(this).dialog('close');
};
dlgBtns[Messages.strFromGeneralLog].click = function () {
loadLog('general', min, max);
$(this).dialog('close');
};
$logAnalyseDialog.dialog({
classes: {
'ui-dialog-titlebar-close': 'btn-close'
},
width: 'auto',
height: 'auto',
buttons: dlgBtns
});
Functions.addDatepicker($dateStart, 'datetime', {
showMillisec: false,
showMicrosec: false,
timeFormat: 'HH:mm:ss',
firstDay: firstDayOfCalendar
});
Functions.addDatepicker($dateEnd, 'datetime', {
showMillisec: false,
showMicrosec: false,
timeFormat: 'HH:mm:ss',
firstDay: firstDayOfCalendar
});
$dateStart.datepicker('setDate', min);
$dateEnd.datepicker('setDate', max);
}
function loadLog(type, min, max) {
var dateStart = Date.parse($('#logAnalyseDialog').find('input[name="dateStart"]').datepicker('getDate')) || min;
var dateEnd = Date.parse($('#logAnalyseDialog').find('input[name="dateEnd"]').datepicker('getDate')) || max;
loadLogStatistics({
src: type,
start: dateStart,
end: dateEnd,
removeVariables: $('#removeVariables').prop('checked'),
limitTypes: $('#limitTypes').prop('checked')
});
}
/* Called in regular intervals, this function updates the values of each chart in the grid */
function refreshChartGrid() {
/* Send to server */
runtime.refreshRequest = $.post('index.php?route=/server/status/monitor/chart', {
'ajax_request': true,
'requiredData': JSON.stringify(runtime.dataList),
'server': CommonParams.get('server')
}, function (data) {
var chartData;
if (typeof data !== 'undefined' && data.success === true) {
chartData = data.message;
} else {
return serverResponseError();
}
var value;
var i = 0;
var diff;
var total;
/* Update values in each graph */
$.each(runtime.charts, function (orderKey, elem) {
var key = elem.chartID;
// If newly added chart, we have no data for it yet
if (!chartData[key]) {
return;
}
// Draw all series
total = 0;
for (var j = 0; j < elem.nodes.length; j++) {
// Update x-axis
if (i === 0 && j === 0) {
if (oldChartData === null) {
diff = chartData.x - runtime.xmax;
} else {
diff = parseInt(chartData.x - oldChartData.x, 10);
}
runtime.xmin += diff;
runtime.xmax += diff;
}
// elem.chart.xAxis[0].setExtremes(runtime.xmin, runtime.xmax, false);
/* Calculate y value */
// If transform function given, use it
if (elem.nodes[j].transformFn) {
value = chartValueTransform(elem.nodes[j].transformFn, chartData[key][j],
// Check if first iteration (oldChartData==null), or if newly added chart oldChartData[key]==null
oldChartData === null || oldChartData[key] === null || oldChartData[key] === undefined ? null : oldChartData[key][j]);
// Otherwise use original value and apply differential and divisor if given,
// in this case we have only one data point per series - located at chartData[key][j][0]
} else {
value = parseFloat(chartData[key][j][0].value);
if (elem.nodes[j].display === 'differential') {
if (oldChartData === null || oldChartData[key] === null || oldChartData[key] === undefined) {
continue;
}
value -= oldChartData[key][j][0].value;
}
if (elem.nodes[j].valueDivisor) {
value = value / elem.nodes[j].valueDivisor;
}
}
// Set y value, if defined
if (value !== undefined) {
elem.chart.series[j].data.push([chartData.x, value]);
if (value > elem.maxYLabel) {
elem.maxYLabel = value;
} else if (elem.maxYLabel === 0) {
elem.maxYLabel = 0.5;
}
// free old data point values and update maxYLabel
if (elem.chart.series[j].data.length > runtime.gridMaxPoints && elem.chart.series[j].data[0][0] < runtime.xmin) {
// check if the next freeable point is highest
if (elem.maxYLabel <= elem.chart.series[j].data[0][1]) {
elem.chart.series[j].data.splice(0, elem.chart.series[j].data.length - runtime.gridMaxPoints);
elem.maxYLabel = getMaxYLabel(elem.chart.series[j].data);
} else {
elem.chart.series[j].data.splice(0, elem.chart.series[j].data.length - runtime.gridMaxPoints);
}
}
if (elem.title === Messages.strSystemMemory || elem.title === Messages.strSystemSwap) {
total += value;
}
}
}
// update chart options
// keep ticks number/positioning consistent while refreshrate changes
var tickInterval = (runtime.xmax - runtime.xmin) / 5;
elem.chart.axes.xaxis.ticks = [runtime.xmax - tickInterval * 4, runtime.xmax - tickInterval * 3, runtime.xmax - tickInterval * 2, runtime.xmax - tickInterval, runtime.xmax];
if (elem.title !== Messages.strSystemCPUUsage && elem.title !== Messages.strQueryCacheEfficiency && elem.title !== Messages.strSystemMemory && elem.title !== Messages.strSystemSwap) {
elem.chart.axes.yaxis.max = Math.ceil(elem.maxYLabel * 1.1);
elem.chart.axes.yaxis.tickInterval = Math.ceil(elem.maxYLabel * 1.1 / 5);
} else if (elem.title === Messages.strSystemMemory || elem.title === Messages.strSystemSwap) {
elem.chart.axes.yaxis.max = Math.ceil(total * 1.1 / 100) * 100;
elem.chart.axes.yaxis.tickInterval = Math.ceil(total * 1.1 / 5);
}
i++;
if (runtime.redrawCharts) {
elem.chart.replot();
}
});
oldChartData = chartData;
runtime.refreshTimeout = setTimeout(refreshChartGrid, monitorSettings.gridRefresh);
});
}
/* Function to get highest plotted point's y label, to scale the chart,
* TODO: make jqplot's autoscale:true work here
*/
function getMaxYLabel(dataValues) {
var maxY = dataValues[0][1];
$.each(dataValues, function (k, v) {
maxY = v[1] > maxY ? v[1] : maxY;
});
return maxY;
}
/* Function that supplies special value transform functions for chart values */
function chartValueTransform(name, cur, prev) {
switch (name) {
case 'cpu-linux':
if (prev === null) {
return undefined;
}
// cur and prev are datapoint arrays, but containing
// only 1 element for cpu-linux
var newCur = cur[0];
var newPrev = prev[0];
var diffTotal = newCur.busy + newCur.idle - (newPrev.busy + newPrev.idle);
var diffIdle = newCur.idle - newPrev.idle;
return 100 * (diffTotal - diffIdle) / diffTotal;
// Query cache efficiency (%)
case 'qce':
if (prev === null) {
return undefined;
}
// cur[0].value is Qcache_hits, cur[1].value is Com_select
var diffQHits = cur[0].value - prev[0].value;
// No NaN please :-)
if (cur[1].value - prev[1].value === 0) {
return 0;
}
return diffQHits / (cur[1].value - prev[1].value + diffQHits) * 100;
// Query cache usage (%)
case 'qcu':
if (cur[1].value === 0) {
return 0;
}
// cur[0].value is Qcache_free_memory, cur[1].value is query_cache_size
return 100 - cur[0].value / cur[1].value * 100;
}
return undefined;
}
/* Build list of nodes that need to be retrieved from server.
* It creates something like a stripped down version of the runtime.charts object.
*/
function buildRequiredDataList() {
runtime.dataList = {};
// Store an own id, because the property name is subject of reordering,
// thus destroying our mapping with runtime.charts <=> runtime.dataList
var chartID = 0;
$.each(runtime.charts, function (key, chart) {
runtime.dataList[chartID] = [];
for (var i = 0, l = chart.nodes.length; i < l; i++) {
runtime.dataList[chartID][i] = chart.nodes[i].dataPoints;
}
runtime.charts[key].chartID = chartID;
chartID++;
});
}
/* Loads the log table data, generates the table and handles the filters */
function loadLogStatistics(opts) {
var logRequest = null;
if (!opts.removeVariables) {
opts.removeVariables = false;
}
if (!opts.limitTypes) {
opts.limitTypes = false;
}
$('#emptyDialog').dialog({
classes: {
'ui-dialog-titlebar-close': 'btn-close'
},
title: Messages.strAnalysingLogsTitle
});
$('#emptyDialog').html(Messages.strAnalysingLogs + '

');
var dlgBtns = {
[Messages.strCancelRequest]: {
text: Messages.strCancelRequest,
class: 'btn btn-primary'
}
};
dlgBtns[Messages.strCancelRequest].click = function () {
if (logRequest !== null) {
logRequest.abort();
}
$(this).dialog('close');
};
$('#emptyDialog').dialog({
classes: {
'ui-dialog-titlebar-close': 'btn-close'
},
width: 'auto',
height: 'auto',
buttons: dlgBtns
});
var url = 'index.php?route=/server/status/monitor/slow-log';
if (opts.src === 'general') {
url = 'index.php?route=/server/status/monitor/general-log';
}
logRequest = $.post(url, {
'ajax_request': true,
'time_start': Math.round(opts.start / 1000),
'time_end': Math.round(opts.end / 1000),
'removeVariables': opts.removeVariables,
'limitTypes': opts.limitTypes,
'server': CommonParams.get('server')
}, function (data) {
var logData;
var dlgBtns = {
[Messages.strClose]: {
text: Messages.strClose,
class: 'btn btn-primary'
}
};
if (typeof data !== 'undefined' && data.success === true) {
logData = data.message;
} else {
return serverResponseError();
}
if (logData.rows.length === 0) {
$('#emptyDialog').dialog({
classes: {
'ui-dialog-titlebar-close': 'btn-close'
},
title: Messages.strNoDataFoundTitle
});
$('#emptyDialog').html('
' + Messages.strNoDataFound + '
');
dlgBtns[Messages.strClose].click = function () {
$(this).dialog('close');
};
$('#emptyDialog').dialog('option', 'buttons', dlgBtns);
return;
}
runtime.logDataCols = buildLogTable(logData, opts.removeVariables);
/* Show some stats in the dialog */
$('#emptyDialog').dialog({
classes: {
'ui-dialog-titlebar-close': 'btn-close'
},
title: Messages.strLoadingLogs
});
$('#emptyDialog').html('
' + Messages.strLogDataLoaded + '
');
$.each(logData.sum, function (key, value) {
var newKey = key.charAt(0).toUpperCase() + key.slice(1).toLowerCase();
if (newKey === 'Total') {
newKey = '
' + newKey + '';
}
$('#emptyDialog').append(newKey + ': ' + value + '
');
});
/* Add filter options if more than a bunch of rows there to filter */
if (logData.numRows > 12) {
$('#logTable').prepend('