The branch, master has been updated via 262cced72f89788a664bbd93d11d945d2428050e (commit) via 41bbe32b12650470306915002f2d0ce4d6ca07bc (commit) via 218d31aefc471e1b9db45bb3484a6d5a378e94bc (commit) via 63dd549550997d4f430e54837bbdcf259948450f (commit) via 5ec242bc149ba1e9ac8ce9499bd8fe17ef0ef75b (commit) via 30e303f214e723dbee4c5498984bce77db688383 (commit) via b04ca6294ac151c8d71bdcb6ca8320473f6a906a (commit) via 1a5c8ccbf725480833f2362aa88ae469dccee46e (commit) via a2b6df6fb7b5a8df7891076c094bfa7e8cd41c91 (commit) via 3224527e0dbc51d5b81844a7254a9f902360ea88 (commit) via 00172039455698a7e798a440cb29e3ea53bfe425 (commit) via 29168e1a0abe3ccb548022b97b3c8509ae7ec6e9 (commit) via b0290ca1bf58700c71654120daaee62d9d76a069 (commit) via 05dd085aeb0334d1e9ee68037efb563866892164 (commit) via 5fb7f9cd1716ae747a6e898ee6e92c864d4da6dc (commit) via 01e185831ebe86886f686edfc95c0e642c95843c (commit) via a4b916e6cd0e25225d6a261d6227b4e46e33f886 (commit) via 8fb2eacb666a67ada3163f69bec5463d9482fde3 (commit) via 74ef217ffd7c17682a47e54c40f17864a9157b56 (commit) via bbd1d9f26ef45ffa90354ce47023ec8c9d427a05 (commit) via 48d35cc3330f5af396b9478f84897a3e064b77fe (commit) via f740cfb2bc7db3293d8fa2f6af008a413915d81c (commit) via baa40842ed4a3ce04bc2d26287d63a01a638e5fd (commit) via 04613d5b1f4c2b118902e22f4bfa6382f2cd9931 (commit) via 6d7244eeea5a38c7215b8ef3cb87995b10dcb612 (commit) from 0d9a0cb42bb864be0e69579e4e342d801288258d (commit)
- Log ----------------------------------------------------------------- commit 262cced72f89788a664bbd93d11d945d2428050e Author: Michal Čihař mcihar@suse.cz Date: Thu Aug 4 13:59:28 2011 +0200
Coding style
commit 41bbe32b12650470306915002f2d0ce4d6ca07bc Merge: 0d9a0cb42bb864be0e69579e4e342d801288258d 218d31aefc471e1b9db45bb3484a6d5a378e94bc Author: Michal Čihař mcihar@suse.cz Date: Thu Aug 4 13:58:20 2011 +0200
Merge remote-tracking branch 'tyron/master'
Conflicts: js/functions.js
commit 218d31aefc471e1b9db45bb3484a6d5a378e94bc Author: Tyron Madlener tyronx@gmail.com Date: Thu Aug 4 11:16:41 2011 +0300
Advisor Fixes
commit 63dd549550997d4f430e54837bbdcf259948450f Author: Tyron Madlener tyronx@gmail.com Date: Thu Aug 4 10:43:57 2011 +0300
- Status variables, new filter: 'Show unformatted values' - Advisory System v0.1 - A simple rules engine implementation (advisor.lib.php) - 51 performance rules based on the mysqltuner 2.0 rules, heavily improved and some newly added (advisory_rules.txt) - 'Advisory' Tab displaying the recommendations
commit 5ec242bc149ba1e9ac8ce9499bd8fe17ef0ef75b Author: Tyron Madlener tyronx@gmail.com Date: Tue Aug 2 11:25:26 2011 +0300
Small improvements: - Byte value also for query_cache_limit - In Status and Server variables, '_' and ' ' can be used interchangably, but only worked for the first occurence - For analysing queries from slow query log, database name is passed on and selected prior to analyzing the query - Timespan format also for Uptime
commit 30e303f214e723dbee4c5498984bce77db688383 Author: Tyron Madlener tyronx@gmail.com Date: Sun Jul 31 16:13:58 2011 +0300
Reapplied Marcs newline removal in messages
commit b04ca6294ac151c8d71bdcb6ca8320473f6a906a Merge: 1a5c8ccbf725480833f2362aa88ae469dccee46e e3b91b7a0bfcb6bc525c9cfb27c8fc7570457b95 Author: Tyron Madlener tyronx@gmail.com Date: Sun Jul 31 16:00:15 2011 +0300
Merge remote-tracking branch 'origin/master'
Conflicts: server_status.php
commit 1a5c8ccbf725480833f2362aa88ae469dccee46e Author: Tyron Madlener tyronx@gmail.com Date: Sun Jul 31 15:58:15 2011 +0300
Added notice for users of < MySQL 5.1.6 that table logging ist not available
commit a2b6df6fb7b5a8df7891076c094bfa7e8cd41c91 Merge: 3224527e0dbc51d5b81844a7254a9f902360ea88 fdcad5b0642765275958e6459609d02faa70a2f6 Author: Tyron Madlener tyronx@gmail.com Date: Fri Jul 29 18:18:26 2011 +0300
Merge remote-tracking branch 'origin/master'
commit 3224527e0dbc51d5b81844a7254a9f902360ea88 Author: Tyron Madlener tyronx@gmail.com Date: Fri Jul 29 10:11:01 2011 +0300
Avoid js-code injection
commit 00172039455698a7e798a440cb29e3ea53bfe425 Author: Tyron Madlener tyronx@gmail.com Date: Thu Jul 28 19:59:17 2011 +0300
Chart Grid importing and exporting feature.
commit 29168e1a0abe3ccb548022b97b3c8509ae7ec6e9 Author: Tyron Madlener tyronx@gmail.com Date: Wed Jul 27 17:38:15 2011 +0300
Monitor: Chart title and series names now editable
commit b0290ca1bf58700c71654120daaee62d9d76a069 Author: Tyron Madlener tyronx@gmail.com Date: Wed Jul 27 13:16:59 2011 +0300
Collapsible navigation frame: Fix for expand link disappeared after page load
commit 05dd085aeb0334d1e9ee68037efb563866892164 Author: Tyron Madlener tyronx@gmail.com Date: Wed Jul 27 12:57:02 2011 +0300
Icons for collapsible frame
commit 5fb7f9cd1716ae747a6e898ee6e92c864d4da6dc Author: Tyron Madlener tyronx@gmail.com Date: Wed Jul 27 12:56:40 2011 +0300
Feature idea: Collapsible navigation frame
commit 01e185831ebe86886f686edfc95c0e642c95843c Merge: a4b916e6cd0e25225d6a261d6227b4e46e33f886 ad4975b82d6beb67d78c0e64092ef29f98a257e3 Author: Tyron Madlener tyronx@gmail.com Date: Wed Jul 27 11:18:46 2011 +0300
Merge remote-tracking branch 'origin/master'
Conflicts: js/functions.js js/server_status.js server_status.php
commit a4b916e6cd0e25225d6a261d6227b4e46e33f886 Author: Tyron Madlener tyronx@gmail.com Date: Wed Jul 27 11:16:28 2011 +0300
drizzle adjustment
commit 8fb2eacb666a67ada3163f69bec5463d9482fde3 Author: Tyron Madlener tyronx@gmail.com Date: Tue Jul 26 16:25:52 2011 +0300
drizzle support
commit 74ef217ffd7c17682a47e54c40f17864a9157b56 Author: Tyron Madlener tyronx@gmail.com Date: Tue Jul 26 16:03:17 2011 +0300
Query Analyzer Dialog: - Show all explains - Mark bad explain values as red
commit bbd1d9f26ef45ffa90354ce47023ec8c9d427a05 Author: Tyron Madlener tyronx@gmail.com Date: Tue Jul 26 13:35:35 2011 +0300
Added documentation link to profiling status names
commit 48d35cc3330f5af396b9478f84897a3e064b77fe Author: Tyron Madlener tyronx@gmail.com Date: Tue Jul 26 12:47:54 2011 +0300
Monitor fixes: - Long INSERT and UPDATE queries are also truncated for slow_log queries - Truncated queries have a byte count appended instead of "[Array]"
commit f740cfb2bc7db3293d8fa2f6af008a413915d81c Author: Tyron Madlener tyronx@gmail.com Date: Tue Jul 26 12:36:32 2011 +0300
Fixes on Monitor: - Removing chart didn't remove div element - Loading from slow_log never returned any results - slow_log query_time and lock_time wrongly calculated - Grouping by ignoring variables, now also sums up all the count columns for slow query log results - Wrong query profiling information
commit baa40842ed4a3ce04bc2d26287d63a01a638e5fd Author: Tyron Madlener tyronx@gmail.com Date: Tue Jul 26 12:33:11 2011 +0300
Better profiling durations formatting
commit 04613d5b1f4c2b118902e22f4bfa6382f2cd9931 Author: Tyron Madlener tyronx@gmail.com Date: Mon Jul 25 23:44:55 2011 +0300
Fixed slow log as data source
commit 6d7244eeea5a38c7215b8ef3cb87995b10dcb612 Author: Tyron Madlener tyronx@gmail.com Date: Mon Jul 25 22:10:52 2011 +0300
- Live charting: Error message on invalid server response - Fixed bugged CPU chart not displaying anything
-----------------------------------------------------------------------
Summary of changes: chart_export.php => file_echo.php | 36 +- js/common.js | 66 +++ js/functions.js | 18 +- js/highcharts/exporting.js | 5 +- js/jquery/jquery.sortableTable.js | 2 +- js/messages.php | 29 +- js/server_status.js | 618 +++++++++++++++----- js/server_variables.js | 2 +- libraries/advisor.lib.php | 198 +++++++ libraries/advisory_rules.txt | 427 ++++++++++++++ libraries/header.inc.php | 1 + libraries/server_variables_doc.php | 3 +- navigation.php | 1 + server_status.php | 188 +++++-- sql.php | 2 +- themes/original/css/theme_right.css.php | 17 +- themes/pmahomme/css/theme_left.css.php | 7 + themes/pmahomme/css/theme_right.css.php | 27 +- .../img/frame-collapse.png} | Bin 4006 -> 3862 bytes themes/pmahomme/img/frame-expand.png | Bin 0 -> 1130 bytes 20 files changed, 1423 insertions(+), 224 deletions(-) rename chart_export.php => file_echo.php (55%) create mode 100644 libraries/advisor.lib.php create mode 100644 libraries/advisory_rules.txt copy themes/{original/img/bd_spatial.png => pmahomme/img/frame-collapse.png} (67%) create mode 100644 themes/pmahomme/img/frame-expand.png
diff --git a/chart_export.php b/file_echo.php similarity index 55% rename from chart_export.php rename to file_echo.php index c05332a..95689a4 100644 --- a/chart_export.php +++ b/file_echo.php @@ -1,7 +1,7 @@ <?php /* vim: set expandtab sw=4 ts=4 sts=4: */ /** - * "Echo" service to allow force downloading of exported charts (png or svg) + * "Echo" service to allow force downloading of exported charts (png or svg) and server status monitor settings * * @package phpMyAdmin */ @@ -11,24 +11,42 @@ define('PMA_MINIMUM_COMMON', true);
require_once './libraries/common.inc.php';
-if (isset($_REQUEST['filename']) && isset($_REQUEST['image'])) { +if(isset($_REQUEST['filename']) && isset($_REQUEST['image'])) { $allowed = Array( 'image/png'=>'png', 'image/svg+xml'=>'svg');
if (! isset($allowed[$_REQUEST['type']])) exit('Invalid export type');
if (! preg_match("/(".implode("|",$allowed).")$/i", $_REQUEST['filename'])) $_REQUEST['filename'] .= '.' . $allowed[$_REQUEST['type']]; + + downloadHeader($_REQUEST['filename'],$_REQUEST['type']);
- header("Cache-Control: public"); - header("Content-Description: File Transfer"); - header("Content-Disposition: attachment; filename=".$_REQUEST['filename']); - header("Content-Type: ".$_REQUEST['type']); - header("Content-Transfer-Encoding: binary"); - if ($allowed[$_REQUEST['type']] != 'svg') echo base64_decode(substr($_REQUEST['image'], strpos($_REQUEST['image'],',') + 1)); else echo $_REQUEST['image']; + + exit(); +}
-} else exit('Invalid request'); +if(isset($_REQUEST['monitorconfig'])) { + downloadHeader('monitor.cfg','application/force-download'); + echo urldecode($_REQUEST['monitorconfig']); + exit(); +} + +if(isset($_REQUEST['import'])) { + echo '<html><body>' . file_get_contents($_FILES['file']['tmp_name']) . '</body></html>'; + exit(); +} + +exit('Invalid request'); + +function downloadHeader($file,$type) { + header("Cache-Control: public"); + header("Content-Description: File Transfer"); + header("Content-Disposition: attachment; filename=".$file); + header("Content-Type: ".$type); + header("Content-Transfer-Encoding: binary"); +} ?> \ No newline at end of file diff --git a/js/common.js b/js/common.js index b595108..06b1609 100644 --- a/js/common.js +++ b/js/common.js @@ -14,6 +14,8 @@ var querywindow = ''; */ var query_to_load = '';
+var leftFrameWidth = -1; + /** * sets current selected db * @@ -363,3 +365,67 @@ function updateTableTitle( table_link_id, new_title ) {
return false; } + +(function($) { + $.fn.extend({ + dom: function () { + var $this = $(this); + var getDom = function(o) { + if( !o || (!o.contentWindow && !o.contentDocument) ) { + return null; + } + + var doc = (o.contentWindow || o.contentDocument); + + return doc.document || doc; + }; + + var dom = getDom($this[0]); + + return dom === null ? $this : $(dom); + } + }); +})(jQuery); + +$(document).ready(function() { + $('frame#frame_navigation').load(function() { + $('frame#frame_navigation').dom().find('div#frameCollapse').show().click(toggleNavigation); + }); + $('frame#frame_content').load(function() { + $('frame#frame_content').dom().find('div#frameExpand').click(toggleNavigation); + }); +}); + +function toggleNavigation() { + if(leftFrameWidth == -1) leftFrameWidth = $('frame#frame_navigation').width(); + + var newWidth = leftFrameWidth - $('frame#frame_navigation').width(); + var $frameset = $('frameset'); + + if(newWidth == 0) { + $('frame#frame_navigation').dom().find('body').css('width',leftFrameWidth + 'px'); + $('frame#frame_navigation').dom().find('body').css('overflow','hidden'); + } else { + $('frame#frame_content').dom().find('div#frameExpand').hide(); + } + + $('frame#frame_navigation').animate( + { width: newWidth }, + { step: function(now, fx) { + $frameset.attr('cols',fx.now + ',*'); + }, + complete: function() { + if(newWidth != 0) { + $('frame#frame_navigation').dom().find('body').css('width',''); + $('frame#frame_navigation').dom().find('body').css('overflow',''); + } else { + $('frame#frame_content').dom().find('div#frameExpand').show(); + } + } + } + ); +} + +function currentWidth() { + return $('frame#frame_navigation').width(); +} \ No newline at end of file diff --git a/js/functions.js b/js/functions.js index 287195d..b6b7692 100644 --- a/js/functions.js +++ b/js/functions.js @@ -29,6 +29,12 @@ var codemirror_editor = false; */ var chart_activeTimeouts = new Object();
+if(window.parent) { + $(document).ready(function() { + if(window.parent.currentWidth() == 0) + $('div#frameExpand').show(); + }); +}
/** * Add a hidden field to the form to indicate that this will be an @@ -1611,13 +1617,15 @@ function PMA_createProfilingChart(data, options) { // Formats a profiling duration nicely. Used in PMA_createProfilingChart() and server_status.js function PMA_prettyProfilingNum(num, acc) { if (!acc) { - acc = 1; + acc = 2; } acc = Math.pow(10,acc); - if (num*1000 < 0.1) { - num = Math.round(acc*(num*1000*1000))/acc + 'µ' + if (num * 1000 < 0.1) { + num = Math.round(acc * (num * 1000 * 1000)) / acc + 'µ'; } else if (num < 0.1) { - num = Math.round(acc*(num*1000))/acc + 'm' + num = Math.round(acc * (num * 1000)) / acc + 'm'; + } else { + num = Math.round(acc * num) / acc; }
return num + 's'; @@ -2018,7 +2026,7 @@ $(document).ready(function() { $("#result_query .notice").remove(); $("#result_query").prepend((data.message)); $("#copyTable").find("select[name='target_db'] option[value="+data.db+"]").attr('selected', 'selected'); - + //Refresh navigation frame when the table is coppied if (window.parent && window.parent.frame_navigation) { window.parent.frame_navigation.location.reload(); diff --git a/js/highcharts/exporting.js b/js/highcharts/exporting.js index cb04357..6ac3a87 100644 --- a/js/highcharts/exporting.js +++ b/js/highcharts/exporting.js @@ -5,6 +5,9 @@ * (c) 2010 Torstein Hønsi * * License: www.highcharts.com/license + * + * Please Note: This file has been adjusted for use in phpMyAdmin, + * to allow chart exporting without the batik library */
// JSLint options: @@ -104,7 +107,7 @@ defaultOptions.exporting = { //enabled: true, //filename: 'chart', type: 'image/png', - url: 'chart_export.php', + url: 'file_echo.php', width: 800, buttons: { exportButton: { diff --git a/js/jquery/jquery.sortableTable.js b/js/jquery/jquery.sortableTable.js index 2dd1ede..1f4fc91 100644 --- a/js/jquery/jquery.sortableTable.js +++ b/js/jquery/jquery.sortableTable.js @@ -49,7 +49,7 @@ $('table').sortableTable('destroy') - removes all events from the table }, destroy : function( ) { $(this).data('sortableTable').destroy(); - }, + } };
if ( methods[method] ) { diff --git a/js/messages.php b/js/messages.php index 357e745..4d9be93 100644 --- a/js/messages.php +++ b/js/messages.php @@ -98,12 +98,28 @@ $js_messages['strMiB'] = __('MiB'); $js_messages['strKiB'] = __('KiB');
$js_messages['strAverageLoad'] = __('Average load'); +$js_messages['strTotalMemory'] = __('Total memory'); +$js_messages['strCachedMemory'] = __('Cached memory'); +$js_messages['strBufferedMemory'] = __('Buffered memory'); +$js_messages['strFreeMemory'] = __('Free memory'); +$js_messages['strUsedMemory'] = __('Used memory'); + +$js_messages['strTotalSwap'] = __('Total Swap'); +$js_messages['strCachedSwap'] = __('Cached Swap'); +$js_messages['strUsedSwap'] = __('Used Swap'); +$js_messages['strFreeSwap'] = __('Free Swap'); + +$js_messages['strBytesSent'] = __('Bytes sent'); +$js_messages['strBytesReceived'] = __('Bytes received'); +$js_messages['strConnections'] = __('Connections'); +$js_messages['strProcesses'] = __('Processes'); + /* l10n: Questions is the name of a MySQL Status variable */ $js_messages['strQuestions'] = __('Questions'); $js_messages['strTraffic'] = __('Traffic'); $js_messages['strSettings'] = __('Settings'); $js_messages['strRemoveChart'] = __('Remove chart'); -$js_messages['strEditChart'] = __('Edit labels and series'); +$js_messages['strEditChart'] = __('Edit title and labels'); $js_messages['strAddChart'] = __('Add chart to grid'); $js_messages['strClose'] = __('Close'); $js_messages['strAddOneSeriesWarning'] = __('Please add at least one variable to the series'); @@ -156,6 +172,17 @@ $js_messages['strIgnoreWhereAndGroup'] = __('Group queries, ignoring variable da $js_messages['strSumRows'] = __('Sum of grouped rows:'); $js_messages['strTotal'] = __('Total:');
+$js_messages['strLoadingLogs'] = __('Loading logs'); +$js_messages['strRefreshFailed'] = __('Monitor refresh failed'); +$js_messages['strInvalidResponseExplanation'] = __('While requesting new chart data the server returned an invalid response. This is most likely because your session expired. Reloading the page and reentering your credentials should help.'); +$js_messages['strReloadPage'] = __('Reload page'); + +$js_messages['strAffectedRows'] = __('Affected rows: '); + +$js_messages['strFailedParsingConfig'] = __('Failed parsing config file. It doesn't seem to be valid JSON code'); +$js_messages['strFailedBuildingGrid'] = __('Failed building chart grid with imported config. Resetting to default config...'); +$js_messages['strImport'] = __('Import'); + /* For inline query editing */ $js_messages['strGo'] = __('Go'); $js_messages['strCancel'] = __('Cancel'); diff --git a/js/server_status.js b/js/server_status.js index d8f3788..60a3490 100644 --- a/js/server_status.js +++ b/js/server_status.js @@ -217,8 +217,9 @@ $(function() { { x: curVal.x, y: (curVal.y_received - lastVal.y_received) / 1024 }, true, numLoadedPoints >= chartObj.options.realtime.numMaxPoints - ); - } + ); + }, + error: function() { serverResponseError(); } } }
@@ -259,8 +260,9 @@ $(function() { { x: curVal.x, y: curVal.y_proc }, true, numLoadedPoints >= chartObj.options.realtime.numMaxPoints - ); - } + ); + }, + error: function() { serverResponseError(); } } };
@@ -295,7 +297,8 @@ $(function() { true, numLoadedPoints >= chartObj.options.realtime.numMaxPoints ); - } + }, + error: function() { serverResponseError(); } } }; } else { @@ -348,7 +351,7 @@ $(function() { });
$('#filterText').keyup(function(e) { - word = $(this).val().replace('_',' '); + word = $(this).val().replace(/_/g,' ');
if(word.length == 0) textFilter = null; else textFilter = new RegExp("(^|_)" + word,'i'); @@ -362,6 +365,11 @@ $(function() { categoryFilter = $(this).val(); filterVariables(); }); + + $('input#dontFormat').change(function() { + $('#serverstatusvariables td.value span.original').toggle(this.checked); + $('#serverstatusvariables td.value span.formatted').toggle(! this.checked); + });
/* Adjust DOM / Add handlers to the tabs */ function initTab(tab,data) { @@ -545,7 +553,55 @@ $(function() { return pointInfo; }
+ /**** Server config advisor ****/
+ $('a[href="#openAdvisorInstructions"]').click(function() { + $('#advisorInstructionsDialog').dialog(); + }); + + $('a[href="#startAnalyzer"]').click(function() { + var $cnt = $('#statustabs_advisor .tabInnerContent'); + $cnt.html('<img class="ajaxIcon" src="' + pmaThemeImage + 'ajax_clock_small.gif" alt="">'); + + $.get('server_status.php?'+url_query, { ajax_request: true, advisor: true },function(data) { + var $tbody, $tr, str, even = true; + + data = $.parseJSON(data); + $cnt.html('<p><b>Possible performance issues</b></p>'); + if(data.fired.length > 0) { + $cnt.append('<table class="data" id="rulesFired" border="0"><thead><tr><th>Issue</th><th>Recommendation</th></tr></thead><tbody></tbody></table>'); + $tbody = $cnt.find('table#rulesFired'); + $.each(data.fired, function(key,value) { + $tbody.append($tr = $('<tr class="linkElem noclick ' + (even ? 'even' : 'odd') + '"><td>' + value.issue + '</td>' + + '<td>' + value.recommendation + ' </td></tr>')); + even = !even; + + $tr.data('rule',value); + $tr.click(function() { + var rule = $(this).data('rule'); + $('div#emptyDialog').attr('title','Rule details'); + $('div#emptyDialog').html( + '<p><b>Issue:</b><br />' + rule.issue + '</p>' + + '<p><b>Recommendation:</b><br />' + rule.recommendation + '</p>' + + '<p><b>Justification:</b><br />' + rule.justification + '</p>' + + '<p><b>Used variable / formula:</b><br />' + rule.formula + '</p>' + + '<p><b>Test:</b><br />' + rule.test + '</p>' + ); + $('div#emptyDialog').dialog({ + width: 600, + buttons: { + 'Close' : function() { + $(this).dialog('close'); + } + } + }); + }); + }); + } + }); + + return false; + });
/**** Monitor charting implementation ****/ @@ -555,15 +611,10 @@ $(function() { var newChart = null; var chartSpacing;
- // Runtime parameter of the monitor + // Runtime parameter of the monitor, is being fully set in initGrid() var runtime = { // Holds all visible charts in the grid charts: null, - // Current max points per chart (needed for auto calculation) - gridMaxPoints: 20, - // displayed time frame - xmin: -1, - xmax: -1, // Stores the timeout handler so it can be cleared refreshTimeout: null, // Stores the GET request to refresh the charts @@ -573,13 +624,18 @@ $(function() { // To play/pause the monitor redrawCharts: false, // Object that contains a list of nodes that need to be retrieved from the server for chart updates - dataList: [] + dataList: [], + // Current max points per chart (needed for auto calculation) + gridMaxPoints: 20, + // displayed time frame + xmin: -1, + xmax: -1 }; - + var monitorSettings = null;
var defaultMonitorSettings = { - columns: 4, + columns: 3, chartSize: { width: 295, height: 250 }, // Max points in each chart. Settings it to 'auto' sets gridMaxPoints to (chartwidth - 40) / 12 gridMaxPoints: 'auto', @@ -593,20 +649,20 @@ $(function() { var presetCharts = { 'cpu-WINNT': { title: PMA_messages['strSystemCPUUsage'], - nodes: [{ dataType: 'cpu', name: 'loadavg', unit: '%'}] + nodes: [{ dataType: 'cpu', name: PMA_messages['strAverageLoad'], dataPoint: 'loadavg', unit: '%'}] }, 'memory-WINNT': { title: PMA_messages['strSystemMemory'], nodes: [ - { dataType: 'memory', name: 'MemTotal', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, - { dataType: 'memory', name: 'MemUsed', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, + { dataType: 'memory', name: PMA_messages['strTotalMemory'], dataPoint: 'MemTotal', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, + { dataType: 'memory', name: PMA_messages['strUsedMemory'], dataPoint: 'MemUsed', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, ] }, 'swap-WINNT': { title: PMA_messages['strSystemSwap'], nodes: [ - { dataType: 'memory', name: 'SwapTotal', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, - { dataType: 'memory', name: 'SwapUsed', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, + { dataType: 'memory', name: PMA_messages['strTotalSwap'], dataPoint: 'SwapTotal', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, + { dataType: 'memory', name: PMA_messages['strUsedSwap'], dataPoint: 'SwapUsed', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, ] }, 'cpu-Linux': { @@ -615,25 +671,17 @@ $(function() { { dataType: 'cpu', name: PMA_messages['strAverageLoad'], unit: '%', - transformFn: function(cur, prev) { - console.log('cpu-linux chart, transformFn()'); - console.log(cur); - console.log(prev); - if(prev == null) return undefined; - var diff_total = cur.busy + cur.idle - (prev.busy + prev.idle); - var diff_idle = cur.idle - prev.idle; - return 100*(diff_total - diff_idle) / diff_total; - } + transformFn: 'cpu-linux' } ] }, 'memory-Linux': { title: PMA_messages['strSystemMemory'], nodes: [ - { dataType: 'memory', name: 'MemUsed', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, - { dataType: 'memory', name: 'Cached', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, - { dataType: 'memory', name: 'Buffers', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, - { dataType: 'memory', name: 'MemFree', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, + { dataType: 'memory', name: PMA_messages['strUsedMemory'], dataPoint: 'MemUsed', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, + { dataType: 'memory', name: PMA_messages['strCachedMemory'], dataPoint: 'Cached', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, + { dataType: 'memory', name: PMA_messages['strBufferedMemory'], dataPoint: 'Buffers', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, + { dataType: 'memory', name: PMA_messages['strFreeMemory'], dataPoint:'MemFree', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, ], settings: { chart: { @@ -650,9 +698,9 @@ $(function() { 'swap-Linux': { title: PMA_messages['strSystemSwap'], nodes: [ - { dataType: 'memory', name: 'SwapUsed', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, - { dataType: 'memory', name: 'SwapCached', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, - { dataType: 'memory', name: 'SwapFree', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, + { dataType: 'memory', name: PMA_messages['strTotalSwap'], dataPoint: 'SwapUsed', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, + { dataType: 'memory', name: PMA_messages['strCachedSwap'], dataPoint: 'SwapCached', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, + { dataType: 'memory', name: PMA_messages['strFreeSwap'], dataPoint: 'SwapFree', valueDivisor: 1024, unit: PMA_messages['strMiB'] }, ], settings: { chart: { @@ -671,18 +719,18 @@ $(function() { // Default setting defaultChartGrid = { 'c0': { title: PMA_messages['strQuestions'], - nodes: [{ dataType: 'statusvar', name: 'Questions', display: 'differential' }] - }, - 'c1': { + nodes: [{ dataType: 'statusvar', name: PMA_messages['strQuestions'], dataPoint: 'Questions', display: 'differential' }] + }, + 'c1': { title: PMA_messages['strChartConnectionsTitle'], - nodes: [ { dataType: 'statusvar', name: 'Connections', display: 'differential' }, - { dataType: 'proc', name: 'Processes'} ] - }, - 'c2': { + nodes: [ { dataType: 'statusvar', name: PMA_messages['strConnections'], dataPoint: 'Connections', display: 'differential' }, + { dataType: 'proc', name: PMA_messages['strProcesses'], dataPoint: 'processes'} ] + }, + 'c2': { title: PMA_messages['strTraffic'], nodes: [ - { dataType: 'statusvar', name: 'Bytes_sent', display: 'differential', valueDivisor: 1024, unit: PMA_messages['strKiB'] }, - { dataType: 'statusvar', name: 'Bytes_received', display: 'differential', valueDivisor: 1024, unit: PMA_messages['strKiB'] } + { dataType: 'statusvar', name: PMA_messages['strBytesSent'], dataPoint: 'Bytes_sent', display: 'differential', valueDivisor: 1024, unit: PMA_messages['strKiB'] }, + { dataType: 'statusvar', name: PMA_messages['strBytesReceived'], dataPoint: 'Bytes_received', display: 'differential', valueDivisor: 1024, unit: PMA_messages['strKiB'] } ] } }; @@ -706,7 +754,7 @@ $(function() { menuItems: [{ textKey: 'editChart', onclick: function() { - alert('tbi'); + editChart(this); } }, { textKey: 'removeChart', @@ -746,9 +794,6 @@ $(function() { height: 24 }, events: { - start: function() { - // console.log('start.'); - }, // Drop event. The drag child element is moved into the drop element // and vice versa. So the parameters are switched. drop: function(drag, drop, pos) { @@ -922,15 +967,15 @@ $(function() { saveMonitor(); // Save settings
$(this).dialog("close"); - } - + }; + dlgButtons[PMA_messages['strClose']] = function() { newChart = null; $('span#clearSeriesLink').hide(); $('#seriesPreview').html(''); $(this).dialog("close"); - } - + }; + $('div#addChartDialog').dialog({ width:'auto', height:'auto', @@ -941,6 +986,102 @@ $(function() {
return false; }); + + $('a[href="#exportMonitorConfig"]').click(function() { + var gridCopy = {}; + + $.each(runtime.charts, function(key, elem) { + gridCopy[key] = {}; + gridCopy[key].nodes = elem.nodes; + gridCopy[key].settings = elem.settings; + gridCopy[key].title = elem.title; + }); + + var exportData = { + monitorCharts: gridCopy, + monitorSettings: monitorSettings + }; + var $form; + + $('body').append($form = $('<form method="post" action="file_echo.php?'+url_query+'&filename=1" style="display:none;"></form>')); + + $form.append('<input type="hidden" name="monitorconfig" value="' + encodeURI($.toJSON(exportData)) + '">'); + $form.submit(); + $form.remove(); + }); + + $('a[href="#importMonitorConfig"]').click(function() { + $('div#emptyDialog').attr('title','Import monitor configuration'); + $('div#emptyDialog').html('Please select the file you want to import:<br/><form action="file_echo.php?'+url_query+'&import=1" method="post" enctype="multipart/form-data">'+ + '<input type="file" name="file"> <input type="hidden" name="import" value="1"> </form>'); + + var dlgBtns = {}; + + dlgBtns[PMA_messages['strImport']] = function() { + var $iframe, $form; + $('body').append($iframe = $('<iframe id="monitorConfigUpload" style="display:none;"></iframe>')); + var d = $iframe[0].contentWindow.document; + d.open(); d.close(); + mew = d; + + $iframe.load(function() { + var json; + + // Try loading config + try { + json = $.secureEvalJSON($('body',$('iframe#monitorConfigUpload')[0].contentWindow.document).html()); + } catch (err) { + alert(PMA_messages['strFailedParsingConfig']); + $('div#emptyDialog').dialog('close'); + return; + } + + // Basic check, is this a monitor config json? + if(!json || ! json.monitorCharts || ! json.monitorCharts) { + alert(PMA_messages['strFailedParsingConfig']); + $('div#emptyDialog').dialog('close'); + return; + } + + // If json ok, try applying config + try { + window.localStorage['monitorCharts'] = $.toJSON(json.monitorCharts); + window.localStorage['monitorSettings'] = $.toJSON(json.monitorSettings); + rebuildGrid(); + } catch(err) { + alert(PMA_messages['strFailedBuildingGrid']); + // If an exception is thrown, load default again + window.localStorage.removeItem('monitorCharts'); + window.localStorage.removeItem('monitorSettings'); + rebuildGrid(); + } + + $('div#emptyDialog').dialog('close'); + }); + + $("body", d).append($form=$('div#emptyDialog').find('form')); + $form.submit(); + $('div#emptyDialog').append('<img class="ajaxIcon" src="' + pmaThemeImage + 'ajax_clock_small.gif" alt="">'); + }; + + dlgBtns[PMA_messages['strCancel']] = function() { + $(this).dialog('close'); + } + + + $('div#emptyDialog').dialog({ + width: 'auto', + height: 'auto', + buttons: dlgBtns + }); + }); + + $('a[href="#clearMonitorConfig"]').click(function() { + window.localStorage.removeItem('monitorCharts'); + window.localStorage.removeItem('monitorSettings'); + $(this).hide(); + rebuildGrid(); + });
$('a[href="#pauseCharts"]').click(function() { runtime.redrawCharts = ! runtime.redrawCharts; @@ -948,7 +1089,7 @@ $(function() { $(this).html('<img src="themes/dot.gif" class="icon ic_play" alt="" /> ' + PMA_messages['strResumeMonitor']); else { $(this).html('<img src="themes/dot.gif" class="icon ic_pause" alt="" /> ' + PMA_messages['strPauseMonitor']); - if(runtime.charts == null) { + if(! runtime.charts) { initGrid(); $('a[href="#settingsPopup"]').show(); } @@ -1067,9 +1208,9 @@ $(function() { }); } ); - } - - + }; + + loadLogVars();
return false; @@ -1133,11 +1274,12 @@ $(function() {
var serie = { dataType:'statusvar', + dataPoint: $('input#variableInput').attr('value'), name: $('input#variableInput').attr('value'), display: $('input[name="differentialValue"]').attr('checked') ? 'differential' : '' };
- if(serie.name == 'Processes') serie.dataType='proc'; + if(serie.dataPoint == 'Processes') serie.dataType='proc';
if($('input[name="useDivisor"]').attr('checked')) serie.valueDivisor = parseInt($('input[name="valueDivisor"]').attr('value')); @@ -1150,7 +1292,7 @@ $(function() { var str = serie.display == 'differential' ? ', ' + PMA_messages['strDifferential'] : ''; str += serie.valueDivisor ? (', ' + $.sprintf(PMA_messages['strDividedBy'], serie.valueDivisor)) : '';
- $('#seriesPreview').append('- ' + serie.name + str + '<br>'); + $('#seriesPreview').append('- ' + serie.dataPoint + str + '<br>');
newChart.nodes.push(serie);
@@ -1209,7 +1351,7 @@ $(function() { height: $('table#chartGrid tr:nth-child(2) td:nth-child(2)').offset().top - $('table#chartGrid tr:nth-child(1) td:nth-child(1)').offset().top } $('table#chartGrid').html(''); - + /* Add all charts - in correct order */ var keys = []; $.each(runtime.charts, function(key, value) { @@ -1228,11 +1370,57 @@ $(function() {
// Empty cells should keep their size so you can drop onto them $('table#chartGrid tr td').css('width',chartSize().width + 'px'); - - + buildRequiredDataList(); refreshChartGrid(); } + + function destroyGrid() { + if(runtime.charts) + $.each(runtime.charts, function(key, value) { + try { + value.chart.destroy(); + } catch(err) {} + }); + try { + runtime.refreshRequest.abort(); + } catch(err) {} + try { + clearTimeout(runtime.refreshTimeout); + } catch(err) {} + + $('table#chartGrid').html(''); + + runtime.charts = null; + runtime.chartAI = 0; + monitorSettings = null; + } + + function rebuildGrid() { + var oldData = null; + if(runtime.charts) { + oldData = {}; + $.each(runtime.charts, function(key, chartObj) { + for(var i=0; i < chartObj.nodes.length; i++) { + oldData[chartObj.nodes[i].dataPoint] = []; + for(var j=0; j < chartObj.chart.series[i].data.length; j++) + oldData[chartObj.nodes[i].dataPoint].push([chartObj.chart.series[i].data[j].x, chartObj.chart.series[i].data[j].y]); + } + }); + } + + destroyGrid(); + initGrid(); + + if(oldData) { + $.each(runtime.charts, function(key, chartObj) { + for(var j=0; j < chartObj.nodes.length; j++) { + if(oldData[chartObj.nodes[j].dataPoint]) + chartObj.chart.series[j].setData(oldData[chartObj.nodes[j].dataPoint]); + } + }); + } + }
function chartSize() { var wdt = $('div#logTable').innerWidth() / monitorSettings.columns - (monitorSettings.columns - 1) * chartSpacing.width; @@ -1284,8 +1472,8 @@ $(function() { $('#logAnalyseDialog').find('dateStart,dateEnd').datepicker('destroy');
$(this).dialog("close"); - } - + }; + dlgBtns[PMA_messages['strFromGeneralLog']] = function() { var dateStart = Date.parse($('#logAnalyseDialog input[name="dateStart"]').attr('value')) || min; var dateEnd = Date.parse($('#logAnalyseDialog input[name="dateEnd"]').attr('value')) || max; @@ -1301,8 +1489,8 @@ $(function() { $('#logAnalyseDialog').find('dateStart,dateEnd').datepicker('destroy');
$(this).dialog("close"); - } - + }; + $('#logAnalyseDialog').dialog({ width: 'auto', height: 'auto', @@ -1358,7 +1546,7 @@ $(function() {
chartObj.chart = PMA_createChart(settings); chartObj.numPoints = 0; - + if(initialize != true) { runtime.charts['c'+runtime.chartAI] = chartObj; buildRequiredDataList(); @@ -1370,11 +1558,59 @@ $(function() { runtime.chartAI++; }
+ function editChart(chartObj) { + var htmlnode = chartObj.options.chart.renderTo; + if(! htmlnode ) return; + + var chart=null; + var chartKey=null; + $.each(runtime.charts, function(key, value) { + if(value.chart.options.chart.renderTo == htmlnode) { + chart = value; + chartKey = key; + return false; + } + }); + + if(chart == null) return; + + var htmlStr = '<p><b>Chart title: </b> <br/> <input type="text" size="35" name="chartTitle" value="' + chart.title + '" />'; + htmlStr += '</p><p><b>Series:</b> </p><ol>'; + for(var i=0; i<chart.nodes.length; i++) { + htmlStr += '<li><i>' + chart.nodes[i].dataPoint +': </i><br/><input type="text" name="chartSerie-' + i + '" value=" ' + chart.nodes[i].name + '" /></li>'; + } + + dlgBtns = {}; + dlgBtns['Save'] = function() { + runtime.charts[chartKey].title = $('div#emptyDialog input[name="chartTitle"]').attr('value'); + runtime.charts[chartKey].chart.setTitle({ text: runtime.charts[chartKey].title }); + + $('div#emptyDialog input[name*="chartSerie"]').each(function() { + var idx = $(this).attr('name').split('-')[1]; + runtime.charts[chartKey].nodes[idx].name = $(this).attr('value'); + runtime.charts[chartKey].chart.series[idx].name = $(this).attr('value'); + }); + + $(this).dialog('close'); + saveMonitor(); + }; + dlgBtns['Cancel'] = function() { + $(this).dialog('close'); + }; + + $('div#emptyDialog').attr('title','Edit chart'); + $('div#emptyDialog').html(htmlStr+'</ol>'); + $('div#emptyDialog').dialog({ + width: 'auto', + height: 'auto', + buttons: dlgBtns + }); + } + function removeChart(chartObj) { var htmlnode = chartObj.options.chart.renderTo; if(! htmlnode ) return; - - + $.each(runtime.charts, function(key, value) { if(value.chart.options.chart.renderTo == htmlnode) { delete runtime.charts[key]; @@ -1388,7 +1624,7 @@ $(function() { // which throws an error when the chart is destroyed setTimeout(function() { chartObj.destroy(); - $('li#' + htmlnode).remove(); + $('div#' + htmlnode).remove(); },10);
saveMonitor(); // Save settings @@ -1397,10 +1633,15 @@ $(function() { function refreshChartGrid() { /* Send to server */ runtime.refreshRequest = $.post('server_status.php?'+url_query, { ajax_request: true, chart_data: 1, type: 'chartgrid', requiredData: $.toJSON(runtime.dataList) },function(data) { - var chartData = $.parseJSON(data); + var chartData; + try { + chartData = $.parseJSON(data); + } catch(err) { + return serverResponseError(); + } var value, i=0; var diff; - + /* Update values in each graph */ $.each(runtime.charts, function(orderKey, elem) { var key = elem.chartID; @@ -1429,12 +1670,13 @@ $(function() { value = value / elem.nodes[j].valueDivisor;
if(elem.nodes[j].transformFn) { - value = elem.nodes[j].transformFn( - chartData[key][j], - (oldChartData == null) ? null : oldChartData[key][j] + value = chartValueTransform( + elem.nodes[j].transformFn, + chartData[key][j], + (oldChartData == null ? null : oldChartData[key][j]) ); } - + if(value != undefined) elem.chart.series[j].addPoint( { x: chartData.x, y: value }, @@ -1455,6 +1697,17 @@ $(function() { runtime.refreshTimeout = setTimeout(refreshChartGrid, monitorSettings.gridRefresh); }); } + + function chartValueTransform(name,cur,prev) { + switch(name) { + case 'cpu-linux': + if(prev == null) return undefined; + var diff_total = cur.busy + cur.idle - (prev.busy + prev.idle); + var diff_idle = cur.idle - prev.idle; + return 100*(diff_total - diff_idle) / diff_total; + } + return undefined; + }
/* Build list of nodes that need to be retrieved */ function buildRequiredDataList() { @@ -1476,10 +1729,10 @@ $(function() { opts.removeVariables = false; if(! opts.limitTypes) opts.limitTypes = false; + + $('#emptyDialog').html(PMA_messages['strAnalysingLogs'] + ' <img class="ajaxIcon" src="' + pmaThemeImage + 'ajax_clock_small.gif" alt="">');
- $('#loadingLogsDialog').html(PMA_messages['strAnalysingLogs'] + ' <img class="ajaxIcon" src="' + pmaThemeImage + 'ajax_clock_small.gif" alt="">'); - - $('#loadingLogsDialog').dialog({ + $('#emptyDialog').dialog({ width: 'auto', height: 'auto', buttons: { @@ -1502,18 +1755,24 @@ $(function() { removeVariables: opts.removeVariables, limitTypes: opts.limitTypes }, - function(data) { - var logData = $.parseJSON(data); - + function(data) { + var logData; + try { + logData = $.parseJSON(data); + } catch(err) { + return serverResponseError(); + } + if(logData.rows.length != 0) { runtime.logDataCols = buildLogTable(logData);
/* Show some stats in the dialog */ - $('#loadingLogsDialog').html('<p>' + PMA_messages['strLogDataLoaded'] + '</p>'); + $('#emptyDialog').attr('title', PMA_messages['strLoadingLogs']); + $('#emptyDialog').html('<p>' + PMA_messages['strLogDataLoaded'] + '</p>'); $.each(logData.sum, function(key, value) { key = key.charAt(0).toUpperCase() + key.slice(1).toLowerCase(); if(key == 'Total') key = '<b>' + key + '</b>'; - $('#loadingLogsDialog').append(key + ': ' + value + '<br/>'); + $('#emptyDialog').append(key + ': ' + value + '<br/>'); });
/* Add filter options if more than a bunch of rows there to filter */ @@ -1551,19 +1810,19 @@ $(function() { dlgBtns[PMA_messages['strJumpToTable']] = function() { $(this).dialog("close"); $(document).scrollTop($('div#logTable').offset().top); - } - - $('#loadingLogsDialog').dialog( "option", "buttons", dlgBtns); - + }; + + $('#emptyDialog').dialog( "option", "buttons", dlgBtns); + } else { - $('#loadingLogsDialog').html('<p>' + PMA_messages['strNoDataFound'] + '</p>'); - + $('#emptyDialog').html('<p>' + PMA_messages['strNoDataFound'] + '</p>'); + var dlgBtns = {}; - dlgBtns[PMA_messages['strClose']] = function() { - $(this).dialog("close"); - } - - $('#loadingLogsDialog').dialog( "option", "buttons", dlgBtns ); + dlgBtns[PMA_messages['strClose']] = function() { + $(this).dialog("close"); + }; + + $('#emptyDialog').dialog( "option", "buttons", dlgBtns ); } } ); @@ -1574,19 +1833,30 @@ $(function() {
if(val.length == 0) textFilter = null; else textFilter = new RegExp(val, 'i'); - - var rowSum = 0, totalSum = 0; - - var i=0, q; + + var rowSum = 0, totalSum = 0, i=0, q; var noVars = $('div#logTable input#noWHEREData').attr('checked'); var equalsFilter = /([^=]+)=(\d+|(('|"|).*?[^\])\4((\s+)|$))/gi; var functionFilter = /([a-z0-9_]+)(.+?)/gi; var filteredQueries = {}; var filteredQueriesLines = {}; - var hide = false; + var hide = false, rowData; var queryColumnName = runtime.logDataCols[runtime.logDataCols.length - 2]; var sumColumnName = runtime.logDataCols[runtime.logDataCols.length - 1]; - + + var isSlowLog = opts.src == 'slow'; + var columnSums = {}; + + var countRow = function(query, row) { + var cells = row.match(/<td>(.*?)</td>/gi); + if(!columnSums[query]) columnSums[query] = [0,0,0,0]; + + columnSums[query][0] += timeToSec(cells[2].replace(/(<td>|</td>)/gi,'')); + columnSums[query][1] += timeToSec(cells[3].replace(/(<td>|</td>)/gi,'')); + columnSums[query][2] += parseInt(cells[4].replace(/(<td>|</td>)/gi,'')); + columnSums[query][3] += parseInt(cells[5].replace(/(<td>|</td>)/gi,'')); + }; + // We just assume the sql text is always in the second last column, and that the total count is right of it $('div#logTable table tbody tr td:nth-child(' + (runtime.logDataCols.length - 1) + ')').each(function() { if(varFilterChange && $(this).html().match(/^SELECT/i)) { @@ -1604,10 +1874,23 @@ $(function() { filteredQueriesLines[q] = i; $(this).text(q); } - + if(isSlowLog) countRow(q, $(this).parent().html()); + + // Restore original columns } else { - $(this).text($(this).parent().data('query')[queryColumnName]); - $(this).next().text($(this).parent().data('query')[sumColumnName]); + rowData = $(this).parent().data('query'); + + // SQL Text + $(this).text(rowData[queryColumnName]); + // # + $(this).next().text(rowData[sumColumnName]); + // Slow log columns + if(isSlowLog) { + $(this).parent().children('td:nth-child(3)').text(rowData['query_time']); + $(this).parent().children('td:nth-child(4)').text(rowData['lock_time']); + $(this).parent().children('td:nth-child(5)').text(rowData['rows_sent']); + $(this).parent().children('td:nth-child(6)').text(rowData['rows_examined']); + } } }
@@ -1633,22 +1916,29 @@ $(function() { hide = false; i++; }); - + + // Update count values of grouped entries if(varFilterChange) { if(noVars) { + var numCol, row, $table = $('div#logTable table tbody'); $.each(filteredQueriesLines, function(key,value) { - if(filteredQueries[value] <= 1) return; - - var numCol = $('div#logTable table tbody tr:nth-child(' + (value+1) + ')') - .children(':nth-child(' + (runtime.logDataCols.length) + ')'); - + if(filteredQueries[key] <= 1) return; + + row = $table.children('tr:nth-child(' + (value+1) + ')'); + numCol = row.children(':nth-child(' + (runtime.logDataCols.length) + ')'); numCol.text(filteredQueries[key]); + + if(isSlowLog) { + row.children('td:nth-child(3)').text(secToTime(columnSums[key][0])); + row.children('td:nth-child(4)').text(secToTime(columnSums[key][1])); + row.children('td:nth-child(5)').text(columnSums[key][2]); + row.children('td:nth-child(6)').text(columnSums[key][3]); + } }); } - - $('div#logTable table').trigger("update"); - setTimeout(function() { - + + $('div#logTable table').trigger("update"); + setTimeout(function() { $('div#logTable table').trigger('sorton',[[[runtime.logDataCols.length - 1,1]]]); }, 0); } @@ -1667,7 +1957,25 @@ $(function() { removeVariables: true, limitTypes: true });*/ - + + function timeToSec(timeStr) { + var time = timeStr.split(':'); + return parseInt(time[0]*3600) + parseInt(time[1]*60) + parseInt(time[2]); + } + + function secToTime(timeInt) { + hours = Math.floor(timeInt / 3600); + timeInt -= hours*3600; + minutes = Math.floor(timeInt / 60); + timeInt -= minutes*60; + + if(hours < 10) hours = '0' + hours; + if(minutes < 10) minutes = '0' + minutes; + if(timeInt < 10) timeInt = '0' + timeInt; + + return hours + ':' + minutes + ':' + timeInt; + } + function buildLogTable(data) { var rows = data.rows; var cols = new Array(); @@ -1682,8 +1990,8 @@ $(function() { return value.replace(/([.*?])+/g,''); } return value; - } - + }; + for(var i=0; i < rows.length; i++) { if(i == 0) { $.each(rows[0],function(key, value) { @@ -1701,7 +2009,7 @@ $(function() { for(var j=0; j < cols.length; j++) { // Assuming the query column is the second last if(j == cols.length - 2 && rows[i][cols[j]].match(/^SELECT/i)) { - $tRow.append($tCell=$('<td class="analyzableQuery">' + formatValue(cols[j], rows[i][cols[j]]) + '</td>')); + $tRow.append($tCell=$('<td class="linkElem">' + formatValue(cols[j], rows[i][cols[j]]) + '</td>')); $tCell.click(queryAnalyzer); } else $tRow.append('<td>' + formatValue(cols[j], rows[i][cols[j]]) + '</td>'); @@ -1718,13 +2026,17 @@ $(function() {
function queryAnalyzer() { - var query = $(this).parent().data('query')[cols[cols.length-2]]; + var query = $(this).parent().data('query').argument || $(this).parent().data('query').sql_text; + var db = $(this).parent().data('query').db || '';
/* A very basic SQL Formatter. Totally fails in the cases of - - Any string appearance containing a MySQL Keyword, surrounded by whitespaces + - Any string appearance containing a MySQL Keyword, surrounded by whitespaces, e.g. WHERE bar = "This where the formatter fails" - Subqueries too probably */ - // .* selector doesn't includde whitespace, [^] doesn't work in IE8, thus we use [^\0] since the zero-byte char (hopefully) doesn't appear in table names ;) + + // Matches the columns to be selected + // .* selector doesn't include whitespace and we have no PCRE_DOTALL modifier, (.|\s)+ crashes Chrome (reported and confirmed), + // [^]+ results in JS error in IE8, thus we use [^\0]+ for matching each column since the zero-byte char (hopefully) doesn't appear in column names ;) var sLists = query.match(/SELECT\s+[^\0]+\s+FROM\s+/gi); if(sLists) { for(var i=0; i < sLists.length; i++) { @@ -1752,7 +2064,8 @@ $(function() { $.post('server_status.php?'+url_query, { ajax_request: true, query_analyzer: true, - query: codemirror_editor.getValue() + query: codemirror_editor.getValue(), + database: db }, function(data) { data = $.parseJSON(data); var totalTime = 0; @@ -1765,15 +2078,40 @@ $(function() { // Float sux, I'll use table :( $('div#queryAnalyzerDialog div.placeHolder') .html('<table width="100%" border="0"><tr><td class="explain"></td><td class="chart"></td></tr></table>'); + + var explain = '<b>Explain output</b> '+explain_docu; + if(data.explain.length > 1) { + explain += ' ('; + for(var i=0; i < data.explain.length; i++) { + if(i > 0) explain += ', '; + explain += '<a href="#showExplain-' + i + '">' + i + '</a>'; + } + explain += ')'; + } + explain +='<p></p>'; + for(var i=0; i < data.explain.length; i++) { + explain += '<div class="explain-' + i + '"' + (i>0? 'style="display:none;"' : '' ) + '>'; + $.each(data.explain[i], function(key,value) { + value = (value==null)?'null':value; + + if(key == 'type' && value.toLowerCase() == 'all') value = '<span class="attention">' + value +'</span>'; + if(key == 'Extra') value = value.replace(/(using (temporary|filesort))/gi,'<span class="attention">$1</span>'); + explain += key+': ' + value + '<br />'; + }); + explain += '</div>'; + } + + // Since there is such a nice free space below the explain, lets put it here for now + explain += '<p><b>' + PMA_messages['strAffectedRows'] + '</b> ' + data.affectedRows;
- var explain = '<b>Explain output</b><p></p>'; - $.each(data.explain, function(key,value) { - value = (value==null)?'null':value; - - explain += key+': ' + value + '<br />'; - }); $('div#queryAnalyzerDialog div.placeHolder td.explain').append(explain); - + + $('div#queryAnalyzerDialog div.placeHolder a[href*="#showExplain"]').click(function() { + var id = $(this).attr('href').split('-')[1]; + $(this).parent().find('div[class*="explain"]').hide(); + $(this).parent().find('div[class*="explain-' + id + '"]').show(); + }); + if(data.profiling) { var chartData = []; var numberTable = '<table class="queryNums"><thead><tr><th>Status</th><th>Time</th></tr></thead><tbody>'; @@ -1789,9 +2127,9 @@ $(function() { } numberTable += '<tr><td><b>Total time:</b></td><td>' + PMA_prettyProfilingNum(totalTime,2) + '</td></tr>'; numberTable += '</tbody></table>'; - - $('div#queryAnalyzerDialog div.placeHolder td.chart').append('<b>Profiling results</b> (<a href="#showNums">Table</a> | <a href="#showChart">Chart</a>)<br/>' + numberTable + ' <div id="queryProfiling"></div>'); - + + $('div#queryAnalyzerDialog div.placeHolder td.chart').append('<b>Profiling results ' + profiling_docu + '</b> (<a href="#showNums">Table</a>, <a href="#showChart">Chart</a>)<br/>' + numberTable + ' <div id="queryProfiling"></div>'); + $('div#queryAnalyzerDialog div.placeHolder a[href="#showNums"]').click(function() { $('div#queryAnalyzerDialog div#queryProfiling').hide(); $('div#queryAnalyzerDialog table.queryNums').show(); @@ -1883,10 +2221,14 @@ $(function() { $('a[href="#clearMonitorConfig"]').show(); }
- $('a[href="#clearMonitorConfig"]').click(function() { - window.localStorage.removeItem('monitorCharts'); - window.localStorage.removeItem('monitorSettings'); - $(this).hide(); - }); - + function serverResponseError() { + var btns = {}; + btns[PMA_messages['strReloadPage']] = function() { + window.location.reload(); + }; + $('#emptyDialog').attr('title',PMA_messages['strRefreshFailed']); + $('#emptyDialog').html('<img class="icon ic_s_attention" src="themes/dot.gif" alt=""> ' + PMA_messages['strInvalidResponseExplanation']) + $('#emptyDialog').dialog({ buttons: btns }); + } + }); diff --git a/js/server_variables.js b/js/server_variables.js index e9bc1cf..8776440 100644 --- a/js/server_variables.js +++ b/js/server_variables.js @@ -129,7 +129,7 @@ $(function() {
$('#filterText').keyup(function(e) { if($(this).val().length==0) textFilter=null; - else textFilter = new RegExp("(^| )"+$(this).val().replace('_',' '),'i'); + else textFilter = new RegExp("(^| )"+$(this).val().replace(/_/g,' '),'i'); filterVariables(); });
diff --git a/libraries/advisor.lib.php b/libraries/advisor.lib.php new file mode 100644 index 0000000..165f068 --- /dev/null +++ b/libraries/advisor.lib.php @@ -0,0 +1,198 @@ +<?php + +class Advisor { + var $variables; + var $parseResult; + var $runResult; + + function run() { + // HowTo: A simple Advisory system in 3 easy steps. + + // Step 1: Get some variables to evaluate on + $this->variables = array_merge(PMA_DBI_fetch_result('SHOW GLOBAL STATUS', 0, 1), PMA_DBI_fetch_result('SHOW GLOBAL VARIABLES', 0, 1)); + // Step 2: Read and parse the list of rules + $this->parseResult = $this->parseRulesFile(); + // Step 3: Feed the variables to the rules and let them fire. Sets $runResult + $this->runRules(); + + /* echo '<br/><hr>'; + echo 'Total rules: '.count($this->parseResult['rules']).' <br><br>'; + echo '<b>Possible performance issues</b><br/>'; + foreach($this->runResult['fired'] as $rule) { + echo $rule['issue'].'<br />'; + } + echo '<br/><b>Rules not checked due to unmet preconditions</b><br/>'; + foreach($this->runResult['unchecked'] as $rule) { + echo $rule['name'].'<br />'; + } + echo '<br/><b>Rules that didn\'t fire</b><br/>'; + foreach($this->runResult['notfired'] as $rule) { + echo $rule['name'].'<br />'; + } + + if($this->runResult['errors']) + echo 'There were errors while testing the rules.'; + */ + return $this->runResult; + } + + function runRules() { + $this->runResult = array( 'fired' => array(), 'notfired' => array(), 'unchecked'=> array(), 'errors' => array() ); + + foreach($this->parseResult['rules'] as $rule) { + $this->variables['value'] = 0; + $precond = true; + + if(isset($rule['precondition'])) { + try { + $precond = $this->ruleExprEvaluate($rule['precondition']); + } catch (Exception $e) { + $this->runResult['errors'][] = 'Failed evaluating precondition for rule \''.$rule['name'].'\'. PHP threw following error: '.$e->getMessage(); + continue; + } + } + + if(! $precond) + $this->addRule('unchecked', $rule); + else { + try { + $value = $this->ruleExprEvaluate($rule['formula']); + } catch(Exception $e) { + $this->runResult['errors'][] = 'Failed calculating value for rule \''.$rule['name'].'\'. PHP threw following error: '.$e->getMessage(); + continue; + } + + $this->variables['value'] = $value; + + try { + if($this->ruleExprEvaluate($rule['test'])) + $this->addRule('fired', $rule); + else $this->addRule('notfired', $rule); + } catch(Exception $e) { + $this->runResult['errors'][] = 'Failed running test for rule \''.$rule['name'].'\'. PHP threw following error: '.$e->getMessage(); + } + } + } + + return true; + } + + function addRule($type, $rule) { + switch($type) { + case 'notfired': + case 'fired': + $jst = preg_split('/\s*\|\s*/',$rule['justification'],2); + if(count($jst) > 1) { + $jst[0] = preg_replace('/%( |,|\.|$)/','%%\1',$jst[0]); + try { + $str = $this->ruleExprEvaluate('sprintf("'.$jst[0].'",'.$jst[1].')',strlen('sprintf("'.$jst[0].'"')); + } catch (Exception $e) { + $this->runResult['errors'][] = 'Failed formattingstring for rule \''.$rule['name'].'\'. PHP threw following error: '.$e->getMessage(); + return; + } + + $rule['justification'] = $str; + } + break; + } + + $this->runResult[$type][] = $rule; + } + + // Runs a code expression, replacing variable names with their respective values + // ignoreUntil: if > 0, it doesn't replace any variables until that string position, but still evaluates the whole expr + function ruleExprEvaluate($expr, $ignoreUntil) { + if($ignoreUntil > 0) { + $exprIgnore = substr($expr,0,$ignoreUntil); + $expr = substr($expr,$ignoreUntil); + } + $old=$expr; + $expr = preg_replace('/fired\s*\(\s*(\'|")(.*)\1\s*\)/Uie','1',$expr); //isset($this->runResult[\'fired\'] + $expr = preg_replace('/\b(\w+)\b/e','isset($this->variables[\'\1\']) ? (!is_numeric($this->variables[\'\1\']) ? \'"\'.$this->variables[\'\1\'].\'"\' : $this->variables[\'\1\']) : \'\1\'', $expr); + if($ignoreUntil > 0){ + $expr = $exprIgnore . $expr; + } + $value = 0; + $err = 0; + ob_start(); + eval('$value = '.$expr.';'); + $err = ob_get_contents(); + ob_end_clean(); + if($err) throw new Exception(strip_tags($err) . '<br />Executed code: $value = '.$expr.';'); + return $value; + } + + function parseRulesFile() { + $file = file('libraries/advisory_rules.txt'); + $errors = array(); + $rules = array(); + $ruleSyntax = array('name','formula','test','issue','recommendation','justification'); + $numRules = count($ruleSyntax); + $numLines = count($file); + $j = -1; + $ruleLine = -1; + + for($i = 0; $i<$numLines; $i++) { + $line = $file[$i]; + if($line[0] == '#' || $line[0] == "\n") continue; + + // Reading new rule + if(substr($line, 0, 4) == 'rule') { + if($ruleLine > 0) { $errors[] = 'Invalid rule declaration on line '.($i+1). ', expected line '.$ruleSyntax[$ruleLine++].' of previous rule' ; continue; } + $ruleLine = 1; + if(preg_match("/rule\s'(.*)'( \[(.*)\])?$/",$line,$match)) { + $j++; + $rules[$j] = array( 'name' => $match[1]); + if(isset($match[3])) $rules[$j]['precondition'] = $match[3]; + } else { + $errors[] = 'Invalid rule declaration on line '.($i+1); + } + continue; + } else { + if($ruleLine == -1) $errors[] = 'Unexpected characters on line '.($i+1); + } + + // Reading rule lines + if($ruleLine > 0) { + if(!isset($line[0])) continue; // Empty lines are ok + // Non tabbed lines are not + if($line[0] != "\t") { $errors[] = 'Unexpected character on line '.($i+1).'. Expected tab, but found \''.$line[0].'\''; continue; } + $rules[$j][$ruleSyntax[$ruleLine++]] = chop(substr($line,1)); + } + + // Rule complete + if($ruleLine == $numRules) { + $ruleLine = -1; + } + } + + return array('rules' => $rules, 'errors' => $errors); + } +} + +function PMA_bytime($num, $precision) { + $per = ''; + if ($num >= 1) { # per second + $per = "per second"; + } + elseif ($num*60 >= 1) { # per minute + $num = $num*60; + $per = "per minute"; + } + elseif ($num*60*60 >=1 ) { # per hour + $num = $num*60*60; + $per = "per hour"; + } + else { + $num = $num*60*60*24; + $per = "per day"; + } + + $num = round($num, $precision); + + if($num == 0) $num = '<'.pow(10,-$precision); + + return "$num $per"; +} + +?> \ No newline at end of file diff --git a/libraries/advisory_rules.txt b/libraries/advisory_rules.txt new file mode 100644 index 0000000..309c1c1 --- /dev/null +++ b/libraries/advisory_rules.txt @@ -0,0 +1,427 @@ +# phpMyAdmin Advisory rules file +# Use only UNIX style newlines +# This file is being parsed by advisor.lib.php, which should handle syntax errors correctly. +# However, PHP Warnings and the like are being consumed by the phpMyAdmin error handler, so those won't show up +# E.g.: Justification line is empty because you used an unescape percent sign, sprintf() returns an empty string and no warning/error is shown +# +# Rule Syntax: +# 'rule' identifier[the name of the rule] eexpr [an optional precondition] +# expr [variable or value calculation used for the test] +# expr [test, if evaluted to 'true' it fires the rule. Use 'value' to insert the calculated value (without quotes)] +# string [the issue (what is the problem?)] +# string [the recommendation (how do i fix it?)] +# formatted-string '|' comma-seperated-expr [the justification (result of the calculated value / why did this rule fire?)] + +# comma-seperated-expr: expr(,expr)* +# eexpr: [expr] - expr enclosed in [] +# expr: a php code literal with extras: +# - variable names are replaced with their respective values +# - fired('name of rule') is replaced with true/false when given rule has been fired. Note however that this is a very simple rules engine. Rules are only checked in sequential order as they are written down here. If given rule has not been checked yet, fired() will always evaluate to false +# - 'value' is replaced with the calculated value. If it is a string, it will be put within single quotes +# - other than that you may use any php function, initialized variable or constant +# +# identifier: A string enclosed in single quotes +# string: A quoteless string, may contain HTML. Variable names enclosed in curly braces are replaced with links to directly edit this variable. e.g. {tmp_table_size} +# formatted-string: You may use classic php sprintf() string formatting here, the arguments must be appended after a trailing pipe (|) as mentioned in above syntax +# percent signs (%) are automatically escaped (%%) in the following cases: When followed by a space, dot or comma and at the end of the line) +# +# Comments start with # +# + + +# Queries + +rule 'Uptime below one day' + Uptime + value < 86400 + Uptime is less than 1 day, performance tuning may not be accurate. + To have more accurate averages it is recommended to let the server run for longer than a day before running this analyzer + The uptime is only %s | PMA_timespanFormat(Uptime) + +rule 'Questions below 1,000' + Questions + value < 1000 + Fewer than 1,000 questions have been run against this server. The recommendations may not be accurate. + Let the server run for a longer time until it has executed a greater amount of queries. + Current amount of Questions: %s | Questions + +rule '% slow queries' [Questions > 0] + Slow_queries / Questions * 100 + value >= 5 + There is a lot of slow queries compared to the overall amount of Queries. + You might want to increase {long_query_time} or optimize the queries listed in the slow query log + The slow query rate should be below 5%, your value is %s%. | round(value,2) + +rule 'slow query rate' [Questions > 0] + (Slow_queries / Questions * 100) / Uptime + value * 60 * 60 > 1 + There is a high percentage of slow queries compared to the server uptime. + You might want to increase {long_query_time} or optimize the queries listed in the slow query log + You have a slow query rate of %s per hour, you should have less than 1% per hour. | PMA_bytime(value,2) + +rule 'Long query time' + long_query_time + value >= 10 + long_query_time is set to 10 seconds or more, thus only slow queries that take above 10 seconds are logged. + It is suggested to set {long_query_time} to a lower value, depending on your enviroment. Usually a value of 1-5 seconds is suggested. + long_query_time is currently set to %ss. | value + +rule 'Slow query logging' + log_slow_queries + value == 'OFF' + The slow query log is disabled. + Enable slow query logging by setting {log_slow_queries} to 'ON'. This will help troubleshooting badly performing queries. + log_slow_queries is set to 'OFF' + +# +# versions +rule 'Release Series' + version + !PMA_DRIZZLE && substr(value,0,3) != "5.1" + The MySQL server version less then 5.1. + You should upgrade, as MySQL 5.1 has improved performance, and MySQL 5.5 even more so. + Current version: %s | value + +rule 'Minor Version' + version + !PMA_DRIZZLE && substr(value,4,2) < 30 + Version less then 5.1.30 (the first GA release of 5.1). + You should upgrade, as recent versions of MySQL 5.1 have improved performance and MySQL 5.5 even more so. + Current version: %s | value + +rule 'Distribution' + version_comment + preg_match('/source/i',value) + Version is compiled from source, not a MySQL official binary. If you did not compile from source, you may be using a package modified by a distribution. + The MySQL manual only is accurate for official MySQL binaries, not any package distributions (such as RedHat, Debian/Ubuntu etc). + 'source' found in version_comment + +rule 'Distribution' + version_comment + preg_match('/percona/i',value) + The MySQL manual only is accurate for official MySQL binaries. + Percona documentation is at http://www.percona.com/docs/wiki/ + 'percona' found in version_comment + +rule 'MySQL Architecture' + system_memory + value > 3072 && !preg_match('/64/',version_compile_machine) + MySQL is not compiled as a 64-bit package, though your memory capacity is above 3 GiB. + MySQL might not be able to access all of your memory. You might want to consider installing the 64-bit version of MySQL. + Available memory on this host: %s | implode(' ',PMA_formatByteDown(value*1024*1024, 2, 2)) + +# +# Query cache + +# Lame: 'ON' == 0 is true, so you need to compare 'ON' == '0' +rule 'Query cache disabled' + query_cache_size + value == 0 || query_cache_type == 'OFF' || query_cache_type == '0' + The query cache is not enabled. + The query cache is known to greatly improve performance if configured correctly. Enable it by setting {query_cache_size} to a 2 digit MiB value and setting {query_cache_type} to 'ON' + query_cache_size is set to 0 or query_cache_type is set to 'OFF' + +rule 'Query cache efficiency (%)' [Com_select + Qcache_hits > 0 && !fired('Query cache disabled')] + Qcache_hits / (Com_select + Qcache_hits) * 100 + value < 20 + Query cache not running efficiently, it has a low hit rate. + Consider increasing {query_cache_limit}. + The current query cache hit rate of %s% is below 20% | round(value,1) + +rule 'Query Cache usage' [!fired('Query cache disabled')] + 100 - Qcache_free_memory / query_cache_size * 100 + value < 80 + Less than 80% of the query cache is being utilized. + This might be caused by {query_cache_limit} being too low. Flushing the query cache might help as well. + The current ratio of free query cache memory to total query cache size is %s%. It should be above 80% | round(value,1) + +rule 'Query cache fragmentation' [!fired('Query cache disabled')] + Qcache_free_blocks / (Qcache_total_blocks / 2) * 100 + value > 20 + The query cache is considerably fragmented. + Severe fragementation is likely to (further) increase Qcache_lowmem_prunes. This might be caused by many Query cache low memory prunes due to {query_cache_size} being too small. For a immediate but short lived fix you can flush the query cache (might lock the query cache for a long time). Carefully adjusting {query_cache_min_res_unit} to a lower value might help too, e.g. you can set it to the average size of your queries in the cache using this formula: (query_cache_size � qcache_free_memory) / qcache_queries_in_cache + The cache is currently fragmented by %s% , with 100% fragmentation meaning that the query cache is an alternating pattern of free and used blocks. This value should be below 20%. | round(value,1) + +rule 'Query cache low memory prunes' [Qcache_inserts > 0 && !fired('Query cache disabled')] + Qcache_lowmem_prunes / Qcache_inserts * 100 + value > 0.1 + Cached queries are removed due to low query cache memory from the query cache. + You might want to increase {query_cache_size}, however keep in mind that the overhead of maintaining the cache is likely to increase with its size, so do this in small increments and monitor the results. + The ratio of removed queries to inserted queries is %s%. The lower this value is, the better (This rules firing limit: 0.1%) | round(value,1) + +rule 'Query cache max size' [!fired('Query cache disabled')] + query_cache_size + value > 1024 * 128 + The query cache size is above 128 MiB. Big query caches may cause significant overhead that is required to maintain the cache. + Depending on your enviroment, it might be performance increasing to reduce this value. + Current query cache size: %s | implode(' ',PMA_formatByteDown(value, 2, 2)) + +rule 'Query cache min result size' [!fired('Query cache disabled')] + value == 1024*1024 + query_cache_limit + The max size of the result set in the query cache is the default of 1 MiB. + Changing {query_cache_limit} (usually by increasing) may increase efficiency. This variable determines the maximum size a query result may have to be inserted into the query cache. If there are many query results above 1 MiB that are well cacheable (many reads, little writes) then increasing {query_cache_limit} will increase efficiency. Whereas in the case of many query results being above 1 MiB that are not very well cacheable (often invalidated due to table updates) increasing {query_cache_limit} might reduce efficiency. + query_cache_limit is set to 1 MiB + +# +# Sorts +rule '% sorts that cause temporary tales' [Sort_scan + Sort_range > 0] + Sort_merge_passes / (Sort_scan + Sort_range) * 100 + value > 10 + Too many sorts are causing temporary tables. + Consider increasing sort_buffer_size and/or read_rnd_buffer_size, depending on your system memory limits + %s% of all sorts cause temporary tables, this value should be lower than 10%. | round(value,1) + +rule 'rate of sorts that cause temporary tables' + Sort_merge_passes / Uptime + value * 60 * 60 > 1 + Too many sorts are causing temporary tables. + Consider increasing sort_buffer_size and/or read_rnd_buffer_size, depending on your system memory limits + Temporary tables average: %s, this value should be less than 1 per hour. | PMA_bytime(value,2) + +rule 'Sort rows' + Sort_rows / Uptime + value * 60 >= 1 + There are lots of rows being sorted. + While there is nothing wrong with a high amount of row sorting, you might want to make sure that the queries which require a lot of sorting use indexed fields in the ORDER BY clause, as this will result in much faster sorting + Sorted rows average: %s | PMA_bytime(value,2) + +# Joins, scans +rule 'rate of joins without indexes' + (Select_range_check + Select_scan + Select_full_join) / Uptime + value * 60 * 60 > 1 + There are too many joins without indexes. + This means that joins are doing full table scans. Adding indexes for the fields being used in the join conditions will greatly speed up the table joins + Table joins average: %s, this value should be less than 1 per hour | PMA_bytime(value,2) + +rule 'rate of reading first index entry' + Handler_read_first / Uptime + value * 60 * 60 > 1 + The rate of reading the first index entry is high. + This usually indicates frequent full index scans. Full index scans are faster than table scans but require lots of cpu cycles in big tables, if those tables that have or had high volumes of UPDATEs and DELETEs, running 'OPTIMIZE TABLE' might reduce the amount of and/or speed up full index scans. Other than that full index scans can only be reduced by rewriting queries. + Index scans average: %s, this value should be less than 1 per hour | PMA_bytime(value,2) + +rule 'rate of reading fixed position' + Handler_read_rnd / Uptime + value * 60 * 60 > 1 + The rate of reading data from a fixed position is high. + This indicates many queries need to sort results and/or do a full table scan, including join queries that do not use indexes. Add indexes where applicable. + Rate of reading fixed position average: %s, this value should be less than 1 per hour | PMA_bytime(value,2) + +rule 'rate of reading next table row' + Handler_read_rnd_next / Uptime + value * 60 * 60 > 1 + The rate of reading the next table row is high. + This indicates many queries are doing full table scans. Add indexes where applicable. + Rate of reading next table row: %s, this value should be less than 1 per hour | PMA_bytime(value,2) + +# temp tables +rule 'tmp_table_size vs. max_heap_table_size' + tmp_table_size - max_heap_table_size + value !=0 + tmp_table_size and max_heap_table_size are not the same. + If you have deliberatly changed one of either: The server uses the lower value of either to determine the maximum size of in-memory tables. So if you wish to increse the in-memory table limit you will have to increase the other value as well. + Current values are tmp_table_size: %s, max_heap_table_size: %s | implode(' ',PMA_formatByteDown(tmp_table_size, 2, 2)), implode(' ',PMA_formatByteDown(max_heap_table_size, 2, 2)) + +rule '% temp disk tables' [Created_tmp_tables + Created_tmp_disk_tables > 0] + Created_tmp_disk_tables / (Created_tmp_tables + Created_tmp_disk_tables) * 100 + value > 25 + Many temporary tables are being written to disk instead of being kept in memory. + Increasing {max_heap_table_size} and {tmp_table_size} might help. However some temporary tables are always being written to disk, independent of the value of these variables. To elminiate these you will have to rewrite your queries to avoid those conditions (Within a temprorary table: Presence of a BLOB or TEXT column or presence of a column bigger than 512 bytes) as mentioned in the beginning of an <a href="http://www.facebook.com/note.php?note_id=10150111255065841&comments">Article by the Pythian Group</a> + %s% of all temporary tables are being written to disk, this value should be below 25% | round(value,1) + +rule 'temp disk rate' + Created_tmp_disk_tables / Uptime + value * 60 * 60 > 1 + Many temporary tables are being written to disk instead of being kept in memory. + Increasing {max_heap_table_size} and {tmp_table_size} might help. However some temporary tables are always being written to disk, independent of the value of these variables. To elminiate these you will have to rewrite your queries to avoid those conditions (Within a temprorary table: Presence of a BLOB or TEXT column or presence of a column bigger than 512 bytes) as mentioned in in the <a href="http://dev.mysql.com/doc/refman/5.0/en/internal-temporary-tables.html">MySQL Documentation</a> + Rate of temporay tables being written to disk: %s, this value should be less than 1 per hour | PMA_bytime(value,2) + +# I couldn't find any source on the internet that suggests a direct relation between high counts of temporary tables and any of these variables. +# Several independent Blog entries suggest (http://ronaldbradford.com/blog/more-on-understanding-sort_buffer_size-2010-0... and http://www.xaprb.com/blog/2010/05/09/how-to-tune-mysqls-sort_buffer_size/) +# that sort_buffer_size should be left as it is. And increasing read_buffer_size is only suggested when there are a lot of +# table scans (http://dev.mysql.com/doc/refman/5.1/en/server-system-variables.html#sysvar_r... and other sources) though +# setting it too high is bad too (http://www.mysqlperformanceblog.com/2007/09/17/mysql-what-read_buffer_size-v...). +#rule 'temp table rate' +# Created_tmp_tables / Uptime +# value * 60 * 60 > 1 +# Many intermediate temporary tables are being created. +# This may be caused by queries under certain conditions as mentioned in the <a href="http://dev.mysql.com/doc/refman/5.0/en/internal-temporary-tables.html">MySQL Documentation</a>. Consider increasing {sort_buffer_size} (sorting), {read_rnd_buffer_size} (random read buffer, ie, post-sort), {read_buffer_size} (sequential scan). + +# +# MyISAM index cache +rule 'MyISAM key buffer size' + key_buffer_size + value == 0 + Key buffer is not initialized. No MyISAM indexes will be cached. + Set {key_buffer_size} depending on the size of your MyISAM indexes. 64M is a good start. + key_buffer_size is 0 + +rule 'max % MyISAM key buffer ever used' [key_buffer_size > 0] + Key_blocks_used * key_cache_block_size / key_buffer_size * 100 + value < 95 + MyISAM key buffer (index cache) % used is low. + You may need to decrease the size of {key_buffer_size}, re-examine your tables to see if indexes have been removed, or examine queries and expectations about what indexes are being used. + max % MyISAM key buffer ever used: %s, this value should be above 95% | round(value,1) + +# Don't fire if above rule fired - we don't need the same advice twice +rule '% MyISAM key buffer used' [key_buffer_size > 0 && !fired('max % MyISAM key buffer ever used')] + ( 1 - Key_blocks_unused * key_cache_block_size / key_buffer_size) * 100 + value < 95 + MyISAM key buffer (index cache) % used is low. + You may need to decrease the size of {key_buffer_size}, re-examine your tables to see if indexes have been removed, or examine queries and expectations about what indexes are being used. + % MyISAM key buffer used: %s, this value should be above 95% | round(value,1) + +rule '% index reads from memory' [Key_read_requests > 0] + 100 - (Key_reads / Key_read_requests * 100) + value < 95 + The % of indexes that use the MyISAM key buffer is low. + You may need to increase {key_buffer_size}. + Index reads from memory: %s%, this value should be above 95% | round(value,1) + +# +# other caches +rule 'rate of table open' + Opened_tables / Uptime + value*60*60 > 10 + The rate of opening tables is high. + Opening tables requires disk I/O which is costly. Increasing {table_open_cache} might avoid this. + Opened table rate: %s, this value should be less than 10 per hour | PMA_bytime(value,2) + +rule '% open files' + Open_files / open_files_limit * 100 + value > 85 + The number of open files is approaching the max number of open files. You may get a "Too many open files" error. + Consider increasing {open_files_limit}, and check the error log when restarting after changing open_files_limit. + The number of opened files is at %s% of the limit. It should be below 85% | round(value,1) + +rule 'rate of open files' + Open_files / Uptime + value * 60 * 60 > 5 + The rate of opening files is high. + Consider increasing {open_files_limit}, and check the error log when restarting after changing open_files_limit. + Opened files rate: %s, this value should be less than 5 per hour | PMA_bytime(value,2) + +rule 'Immediate table locks %' [Table_locks_waited + Table_locks_immediate > 0] + Table_locks_immediate / (Table_locks_waited + Table_locks_immediate) * 100 + value < 95 + Too many table locks were not granted immediately. + Optimize queries and/or use InnoDB to reduce lock wait. + Immediate table locks: %s%, this value should be above 95% | round(value,1) + +rule 'Table lock wait rate' + Table_locks_waited / Uptime + value * 60 * 60 > 1 + Too many table locks were not granted immediately. + Optimize queries and/or use InnoDB to reduce lock wait. + Table lock wait rate: %s, this value should be less than 1 per hour | PMA_bytime(value,2) + +rule 'thread cache' + thread_cache_size + value < 1 + Thread cache is disabled, resulting in more overhead from new connections to MySQL. + Enable the thread cache by setting {thread_cache_size} > 0. + The thread cache is set to 0 + +rule 'thread cache hit rate %' [thread_cache_size > 0] + 100 - Threads_created / Connections + value < 80 + Thread cache is not efficient. + Increase {thread_cache_size}. + Thread cache hitrate: %s%, this value should be above 80% | round(value,1) + +rule 'Threads that are slow to launch' [slow_launch_time > 0] + Slow_launch_threads + value > 0 + There are too many threads that are slow to launch. + This generally happens in case of general system overload as it is pretty simple operations. You might want to monitor your system load carefully. + %s thread(s) took longer than %s seconds to start, it should be 0 | value, slow_launch_time + +rule 'Slow launch time' + slow_launch_time + value > 2 + Slow_launch_threads is above 2s + Set slow_launch_time to 1s or 2s to correctly count threads that are slow to launch + slow_launch_time is set to %s | value + +# +#Connections +rule '% connections used' + Max_used_connections / max_connections * 100 + value > 80 + The maximum amount of used connnections is getting close to the value of max_connections. + Increase max_connections, or decrease wait_timeout so that connections that do not close database handlers properly get killed sooner. Make sure the code closes database handlers properly. + Max_used_connections is at %s% of max_connections, it should be below 80% | round(value,1) + +rule '% aborted connections' + Aborted_connects / Connections * 100 + value > 1 + Too many connections are aborted. + Connections are usually aborted when they cannot be authorized. <a href="http://www.mysqlperformanceblog.com/2008/08/23/how-to-track-down-the-source-of-aborted_connects/">This article</a> might help you track down the source. + %s% of all connections are aborted. This value should be below 1% | round(value,1) + +rule 'rate of aborted connections' + Aborted_connects / Uptime + value * 60 * 60 > 1 + Too many connections are aborted + Connections are usually aborted when they cannot be authorized. <a href="http://www.mysqlperformanceblog.com/2008/08/23/how-to-track-down-the-source-of-aborted_connects/">This article</a> might help you track down the source. + Aborted connections rate is at %s, this value should be less than 1 per hour | PMA_bytime(value,2) + +rule '% aborted clients' + Aborted_clients / Connections * 100 + value > 2 + Too many clients are aborted. + Clients are usually aborted when they did not close their connection to MySQL properly. This can be due to network issues or code not closing a database handler properly. Check your network and code. + %s% of all clients are aborted. This value should be below 2% | round(value,1) + +rule 'rate of aborted clients' + Aborted_clients / Uptime + value * 60 * 60 > 1 + Too many clients are aborted. + Clients are usually aborted when they did not close their connection to MySQL properly. This can be due to network issues or code not closing a database handler properly. Check your network and code. + Aborted client rate is at %s, this value should be less than 1 per hour | PMA_bytime(value,2) + +# +# InnoDB +rule 'Is InnoDB disabled?' + have_innodb + value != "YES" + You do not have InnoDB enabled. + InnoDB is usually the better choice for table engines. + have_innodb is set to 'value' + +rule '% InnoDB log size' [innodb_buffer_pool_size > 0] + innodb_log_file_size / innodb_buffer_pool_size * 100 + value < 20 + The InnoDB log file size is not an appropriate size, in relation to the InnoDB buffer pool. + Especiallay one a system with a lot of writes to InnoDB tables you shoud set innodb_log_file_size to 25% of {innodb_buffer_pool_size}. However the bigger this value, the longer the recovery time will be when database crashes, so this value should not be set much higher than 256 MiB. Please note however that you cannot simply change the value of this variable. You need to shutdown the server, remove the InnoDB log files, set the new value in my.cnf, start the server, then check the error logs if everything went fine. See also <a href="http://mysqldatabaseadministration.blogspot.com/2007/01/increase-innodblogfilesize-proper-way.html">this blog entry</a> + Your InnoDB log size is at %s% in relation to the InnoDB buffer pool size, it should not be below 20% | round(value,1) + +rule 'Max InnoDB log size' [innodb_buffer_pool_size > 0 && innodb_log_file_size / innodb_buffer_pool_size * 100 < 30] + innodb_log_file_size / (1024 * 1024) + value >= 128 + The InnoDB log file size is inadequately large. + It is usually sufficient to set innodb_log_file_size to 25% of the size of {innodb_buffer_pool_size}. A very innodb_log_file_size slows down the recovery time after a database crash considerably. See also <a href="http://www.mysqlperformanceblog.com/2006/07/03/choosing-proper-innodb_log_file_size/">this Article</a>. You need to shutdown the server, remove the InnoDB log files, set the new value in my.cnf, start the server, then check the error logs if everything went fine. See also <a href="http://mysqldatabaseadministration.blogspot.com/2007/01/increase-innodblogfilesize-proper-way.html">this blog entry</a> + Your absolute InnoD log size is %s MiB | round(value,1) + +rule 'InnoDB buffer pool size' [system_memory > 0] + innodb_buffer_pool_size / system_memory * 100 + value < 60 + Your InnoDB buffer pool is fairly small. + The InnoDB buffer pool has a profound impact on perfomance for InnoDB tables. Assign all your remaining memory to this buffer. For database servers that use solely InnoDB as storage engine and have no other services (e.g. a web server) running, you may set this as high as 80% of your available memory. If that is not the case, you need to carefully assess the memory consumption of your other services and non-InnoDB-Tables and set this variable accordingly. If it is set too high, your system will start swapping, which decreases performance significantly. See also <a href="http://www.mysqlperformanceblog.com/2007/11/03/choosing-innodb_buffer_pool_size/">this article</a> + You are currently using %s% of your memory for the InnoDB buffer pool. This rule fires if you are assigning less than 60%, however this might be perfectly adequate for your system if you don't have much InnoDB tables or other services running on the same machine. + +# +# other +rule 'MyISAM concurrent inserts' + concurrent_insert + value == 0 + Enable concurrent_insert by setting it to 1 + Setting {concurrent_insert} to 1 reduces contention between readers and writers for a given table. See also <a href="http://dev.mysql.com/doc/refman/5.0/en/concurrent-inserts.html">MySQL Documentation</a> + concurrent_insert is set to 0 + +# INSERT DELAYED USAGE +#Delayed_errors 0 +#Delayed_insert_threads 0 +#Delayed_writes 0 +#Not_flushed_delayed_rows diff --git a/libraries/header.inc.php b/libraries/header.inc.php index a6ed8ed..ff63999 100644 --- a/libraries/header.inc.php +++ b/libraries/header.inc.php @@ -71,6 +71,7 @@ if (isset($GLOBALS['is_ajax_request']) && !$GLOBALS['is_ajax_request']) { </head>
<body> + <div id="frameExpand" style="display:none;"><img src="themes/pmahomme/img/frame-expand.png"></div> <?php
// Include possible custom headers diff --git a/libraries/server_variables_doc.php b/libraries/server_variables_doc.php index d705d39..dbd19eb 100644 --- a/libraries/server_variables_doc.php +++ b/libraries/server_variables_doc.php @@ -970,7 +970,8 @@ $VARIABLE_DOC_LINKS['query_alloc_block_size'] = array( $VARIABLE_DOC_LINKS['query_cache_limit'] = array( 'query_cache_limit', 'server-system-variables', - 'sysvar'); + 'sysvar', + 'byte'); $VARIABLE_DOC_LINKS['query_cache_min_res_unit'] = array( 'query_cache_min_res_unit', 'server-system-variables', diff --git a/navigation.php b/navigation.php index 0c5668b..f684d11 100644 --- a/navigation.php +++ b/navigation.php @@ -156,6 +156,7 @@ require_once './libraries/header_http.inc.php'; </head>
<body id="body_leftFrame"> +<div id="frameCollapse" style="display:none;"><img src="themes/pmahomme/img/frame-collapse.png"></div> <?php require './libraries/navigation_header.inc.php';
diff --git a/server_status.php b/server_status.php index 877f0a5..d26e044 100644 --- a/server_status.php +++ b/server_status.php @@ -6,7 +6,7 @@ * * @package phpMyAdmin */ - + /** * no need for variables importing * @ignore @@ -88,8 +88,8 @@ if (isset($_REQUEST['ajax_request']) && $_REQUEST['ajax_request'] == true) { switch ($node['dataType']) { case 'statusvar': // Some white list filtering - if (!preg_match('/[^a-zA-Z_]+/',$node['name'])) - $statusVars[] = $node['name']; + if (!preg_match('/[^a-zA-Z_]+/',$node['dataPoint'])) + $statusVars[] = $node['dataPoint']; break;
case 'proc': @@ -121,7 +121,7 @@ if (isset($_REQUEST['ajax_request']) && $_REQUEST['ajax_request'] == true) { if (!$memory) $memory = $sysinfo->memory();
- $ret[$chart_id][$node_id]['y'] = $memory[$node['name']]; + $ret[$chart_id][$node_id]['y'] = $memory[$node['dataPoint']]; break; } } @@ -134,7 +134,7 @@ if (isset($_REQUEST['ajax_request']) && $_REQUEST['ajax_request'] == true) { foreach ($ret as $chart_id => $chartNodes) { foreach ($chartNodes as $node_id => $node) { if ($node['dataType'] == 'statusvar') - $ret[$chart_id][$node_id]['y'] = $vars[$node['name']]; + $ret[$chart_id][$node_id]['y'] = $vars[$node['dataPoint']]; } }
@@ -145,28 +145,46 @@ if (isset($_REQUEST['ajax_request']) && $_REQUEST['ajax_request'] == true) { }
if (isset($_REQUEST['log_data'])) { + if(PMA_MYSQL_INT_VERSION < 50106) exit('""'); + $start = intval($_REQUEST['time_start']); $end = intval($_REQUEST['time_end']);
if ($_REQUEST['type'] == 'slow') { - $q = 'SELECT SUM(query_time) AS TIME(query_time), SUM(lock_time) as lock_time, '. - 'SUM(rows_sent) AS rows_sent, SUM(rows_examined) AS rows_examined, sql_text, COUNT(sql_text) AS '#' '. - 'FROM `mysql`.`slow_log` WHERE event_time > FROM_UNIXTIME('.$start.') '. - 'AND event_time < FROM_UNIXTIME('.$end.') GROUP BY sql_text'; - + $q = 'SELECT start_time, user_host, Sec_to_Time(Sum(Time_to_Sec(query_time))) as query_time, Sec_to_Time(Sum(Time_to_Sec(lock_time))) as lock_time, '. + 'SUM(rows_sent) AS rows_sent, SUM(rows_examined) AS rows_examined, db, sql_text, COUNT(sql_text) AS '#' '. + 'FROM `mysql`.`slow_log` WHERE start_time > FROM_UNIXTIME('.$start.') '. + 'AND start_time < FROM_UNIXTIME('.$end.') GROUP BY sql_text'; + $result = PMA_DBI_try_query($q);
$return = array('rows' => array(), 'sum' => array()); $type = '';
while ($row = PMA_DBI_fetch_assoc($result)) { - $type = substr($row['sql_text'],0,strpos($row['sql_text'],' ')); - $return['sum'][$type]++; + $type = strtolower(substr($row['sql_text'],0,strpos($row['sql_text'],' '))); + + switch($type) { + case 'insert': + case 'update': + // Cut off big inserts and updates, but append byte count therefor + if(strlen($row['sql_text']) > 220) + $row['sql_text'] = substr($row['sql_text'],0,200) . '... [' . + implode(' ',PMA_formatByteDown(strlen($row['sql_text']), 2, 2)).']'; + + break; + default: + break; + } + + if(!isset($return['sum'][$type])) $return['sum'][$type] = 0; + $return['sum'][$type] += $row['#']; $return['rows'][] = $row; }
$return['sum']['TOTAL'] = array_sum($return['sum']); - + $return['numRows'] = count($return['rows']); + PMA_DBI_free_result($result);
exit(json_encode($return)); @@ -220,10 +238,10 @@ if (isset($_REQUEST['ajax_request']) && $_REQUEST['ajax_request'] == true) {
case 'update': // Cut off big inserts and updates, but append byte count therefor - if(strlen($row['argument']) > 180) - $row['argument'] = substr($row['argument'],0,160) . '... [' . - PMA_formatByteDown(strlen($row['argument']), 2).']'; - + if(strlen($row['argument']) > 220) + $row['argument'] = substr($row['argument'],0,200) . '... [' . + implode(' ',PMA_formatByteDown(strlen($row['argument'])), 2, 2).']'; + break;
default: break; @@ -258,21 +276,29 @@ if (isset($_REQUEST['ajax_request']) && $_REQUEST['ajax_request'] == true) {
if(isset($_REQUEST['query_analyzer'])) { $return = array(); + + if(strlen($_REQUEST['database'])) + PMA_DBI_select_db($_REQUEST['database']);
if ($profiling = PMA_profilingSupported()) PMA_DBI_query('SET PROFILING=1;');
// Do not cache query $query = preg_replace('/^(\s*SELECT)/i','\1 SQL_NO_CACHE',$_REQUEST['query']); - + + $result = PMA_DBI_try_query($query); + $return['affectedRows'] = $GLOBALS['cached_affected_rows']; + $result = PMA_DBI_try_query('EXPLAIN ' . $query); - $return['explain'] = PMA_DBI_fetch_assoc($result); - + while ($row = PMA_DBI_fetch_assoc($result)) { + $return['explain'][] = $row; + } + // In case an error happened $return['error'] = PMA_DBI_getError(); - + PMA_DBI_free_result($result); - + if($profiling) { $return['profiling'] = array(); $result = PMA_DBI_try_query('SELECT seq,state,duration FROM INFORMATION_SCHEMA.PROFILING WHERE QUERY_ID=1 ORDER BY seq'); @@ -284,6 +310,12 @@ if (isset($_REQUEST['ajax_request']) && $_REQUEST['ajax_request'] == true) {
exit(json_encode($return)); } + + if(isset($_REQUEST['advisor'])) { + include('libraries/advisor.lib.php'); + $advisor = new Advisor(); + exit(json_encode($advisor->run())); + } }
@@ -397,18 +429,11 @@ if (isset($server_status['Key_reads'])
// Threads_cache_hitrate if (isset($server_status['Threads_created']) - && isset($server_status['Connections']) - && $server_status['Connections'] > 0) { + && isset($server_status['Connections']) + && $server_status['Connections'] > 0) { + $server_status['Threads_cache_hitrate_%'] = - 100 - - $server_status['Threads_created'] - / $server_status['Connections'] - * 100; -} - -// Format Uptime_since_flush_status : show as days, hours, minutes, seconds -if (isset($server_status['Uptime_since_flush_status'])) { - $server_status['Uptime_since_flush_status'] = PMA_timespanFormat($server_status['Uptime_since_flush_status']); + 100 - $server_status['Threads_created'] / $server_status['Connections'] * 100; }
/** @@ -546,8 +571,13 @@ foreach ($server_status as $name => $value) { } }
-// admin commands are not queries (e.g. they include COM_PING, which is excluded from $server_status['Questions']) -unset($used_queries['Com_admin_commands']); +if(PMA_DRIZZLE) { + $used_queries = PMA_DBI_fetch_result('SELECT * FROM data_dictionary.global_statements', 0, 1); + unset($used_queries['admin_commands']); +} else { + // admin commands are not queries (e.g. they include COM_PING, which is excluded from $server_status['Questions']) + unset($used_queries['Com_admin_commands']); +}
/* Ajax request refresh */ if (isset($_REQUEST['show']) && isset($_REQUEST['ajax_request'])) { @@ -598,6 +628,8 @@ server_time_diff = new Date().getTime() - <?php echo microtime(true)*1000; ?>; server_os = '<?php echo PHP_OS; ?>'; is_superuser = <?php echo PMA_isSuperuser()?'true':'false'; ?>; server_db_isLocal = <?php echo ($server_db_isLocal)?'true':'false'; ?>; +profiling_docu = '<?php echo PMA_showMySQLDocu('general-thread-states','general-thread-states'); ?>'; +explain_docu = '<?php echo PMA_showMySQLDocu('explain-output', 'explain-output'); ?>'; </script> <div id="serverstatus"> <h2><?php @@ -617,6 +649,7 @@ echo __('Runtime Information'); <li><a href="#statustabs_queries"><?php echo __('Query statistics'); ?></a></li> <li><a href="#statustabs_allvars"><?php echo __('All status variables'); ?></a></li> <li><a href="#statustabs_charting"><?php echo __('Monitor'); ?></a></li> + <li><a href="#statustabs_advisor"><?php echo __('Advisor'); ?></a></li> </ul>
<div id="statustabs_traffic"> @@ -689,6 +722,10 @@ echo __('Runtime Information'); ?> </select> </div> + <div class="formelement"> + <input type="checkbox" name="dontFormat" id="dontFormat"> + <label for="dontFormat"><?php echo __('Show unformatted values'); ?></label> + </div> </fieldset> <div id="linkSuggestions" class="defaultLinks" style="display:none"> <p class="notice"><?php echo __('Related links:'); ?> @@ -719,6 +756,21 @@ echo __('Runtime Information'); <div id="statustabs_charting"> <?php printMonitor(); ?> </div> + + <div id="statustabs_advisor"> + <p><a href="#startAnalyzer">Start analyzer</a> | <a href="#openAdvisorInstructions">Instructions</a></p> + <div class="tabInnerContent"> + </div> + <div id="advisorInstructionsDialog" style="display:none;"> + <?php echo __('The Advisor system can provide recommendations on server variables by analyzing the server status variables. + Do note however that this system provides recommendations based on fairly simple calculations and by rule of thumb and + may not necessarily work for your system. + Prior to changing any of the configuration, be sure to know what you are changing and how to undo the change. Wrong tuning + can have a very negative effect on performance. + The best way to tune the system would be to change only one setting at a time, observe or benchmark your database, and + undo the change if there was no clearly measurable improvement.'); ?> + </div> + </div> </div> </div>
@@ -1272,7 +1324,7 @@ function printVariablesTable() { <tr class="<?php echo $odd_row ? 'odd' : 'even'; echo isset($allocationMap[$name])?' s_'.$allocationMap[$name]:''; ?>"> <th class="name"><?php echo htmlspecialchars(str_replace('_',' ',$name)) . PMA_showMySQLDocu('server-status-variables', 'server-status-variables', false, 'statvar_' . $name); ?> </th> - <td class="value"><?php + <td class="value"><span class="formatted"><?php if (isset($alerts[$name])) { if ($value > $alerts[$name]) { echo '<span class="attention">'; @@ -1282,6 +1334,8 @@ function printVariablesTable() { } if ('%' === substr($name, -1, 1)) { echo PMA_formatNumber($value, 0, 2) . ' %'; + } elseif (strpos($name,'Uptime')!==FALSE) { + echo PMA_timespanFormat($value); } elseif (is_numeric($value) && $value == (int) $value && $value > 1000) { echo PMA_formatNumber($value, 3, 1); } elseif (is_numeric($value) && $value == (int) $value) { @@ -1294,7 +1348,8 @@ function printVariablesTable() { if (isset($alerts[$name])) { echo '</span>'; } - ?></td> + ?></span><span style="display:none;" class="original"><?php echo $value; ?></span> + </td> <td class="descr"> <?php if (isset($strShowStatus[$name ])) { @@ -1349,13 +1404,14 @@ function printMonitor() { <a href="#addNewChart"> <img src="themes/dot.gif" class="icon ic_b_chart" alt="" /> <?php echo __('Add chart'); ?> - </a> | - <a href="#rearrangeCharts"> <?php echo __('Rearrange/edit charts'); ?></a><br> - <p> - <?php echo __('Refresh rate:'); refreshList('gridChartRefresh'); ?><br> - </p> - <p> - <?php echo __('Chart columns:'); ?> + </a> + <a href="#rearrangeCharts"><img class="icon ic_b_tblops" src="themes/dot.gif" width="16" height="16" alt=""> <?php echo __('Rearrange/edit charts'); ?></a> + <div class="clearfloat paddingtop"></div> + <div class="floatleft"> + <?php echo __('Refresh rate').'<br />'; refreshList('gridChartRefresh', 5, Array(2,3,4,5,10,20,40,60,120,300,600,1200)); ?><br> + </div> + <div class="floatleft"> + <?php echo __('Chart columns'); ?> <br /> <select name="chartColumns"> <option>1</option> <option>2</option> @@ -1368,25 +1424,42 @@ function printMonitor() { <option>9</option> <option>10</option> </select> - </p> - <a href="#clearMonitorConfig"><?php echo __('Clear monitor config'); ?></a> + </div> + + <div class="clearfloat paddingtop"> + <b><?php echo __('Chart arrangement'); ?></b> <?php echo PMA_showHint(__('The arrangement of the charts is stored to the browsers local storage. You may want to export it if you have a complicated set up.')); ?><br/> + <a href="#importMonitorConfig"><?php echo __('Import'); ?></a> <a href="#exportMonitorConfig"><?php echo __('Export'); ?></a> <a href="#clearMonitorConfig"><?php echo __('Reset to default'); ?></a> + </div> </div>
<div id="monitorInstructionsDialog" title="<?php echo __('Monitor Instructions'); ?>" style="display:none;"> <?php echo __('The phpMyAdmin Monitor can assist you in optimizing the server configuration and track down time intensive queries. For the latter you will need to set log_output to \'TABLE\' and have either the slow_query_log or general_log enabled. Note however, that the general_log produces a lot of data and increases server load by up to 15%'); ?> - <p></p> - <img class="ajaxIcon" src="<?php echo $GLOBALS['pmaThemeImage']; ?>ajax_clock_small.gif" alt="Loading"> - <div class="ajaxContent"> - </div> - <div class="monitorUse" style="display:none;"> - <p></p> - <?php echo __('<b>Using the monitor:</b><br/> Ok, you are good to go! Once you click \'Start monitor\' your browser will refresh all displayed charts in a regular interval. You may add charts and change the refresh rate under \'Settings\', or remove any chart using the cog icon on each respective chart.<p>When you get to see a sudden spike in activity, select the relevant time span on any chart by holding down the left mouse button and panning over the chart. This will load statistics from the logs helping you find what caused the activity spike.</p>'); - ?> + <?php if(PMA_MYSQL_INT_VERSION < 50106) { ?> <p> <img class="icon ic_s_attention" src="themes/dot.gif" alt=""> - <?php echo __('<b>Please note:</b> Enabling the general_log may increase the server load by 5-15%. Also be aware that generating statistics from the logs is a load intensive task, so it is advisable to select only a small time span and to disable the general_log and empty its table once monitoring is not required any more.'); ?> + <?php + echo __('Unfortunately your Database server does not support logging to table, which is a requirement for analyzing the database logs with phpMyAdmin. Logging to table is supported by MySQL 5.1.6 and onwards. You may still use the server charting features however.'); + ?> </p> + <?php + } else { + ?> + <p></p> + <img class="ajaxIcon" src="<?php echo $GLOBALS['pmaThemeImage']; ?>ajax_clock_small.gif" alt="Loading"> + <div class="ajaxContent"></div> + <div class="monitorUse" style="display:none;"> + <p></p> + <?php + echo __('<b>Using the monitor:</b><br/> Ok, you are good to go! Once you click \'Start monitor\' your browser will refresh all displayed charts in a regular interval. You may add charts and change the refresh rate under \'Settings\', or remove any chart using the cog icon on each respective chart. <p>To display queries from the logs, select the relevant time span on any chart by holding down the left mouse button and panning over the chart. Once confirmed, this will load a table of grouped queries, there you may click on any occuring SELECT statements to further analyze them.</p>'); + ?> + <p> + <img class="icon ic_s_attention" src="themes/dot.gif" alt=""> + <?php + echo __('<b>Please note:</b> Enabling the general_log may increase the server load by 5-15%. Also be aware that generating statistics from the logs is a load intensive task, so it is advisable to select only a small time span and to disable the general_log and empty its table once monitoring is not required any more.'); + ?> + </p> </div> + <?php } ?> </div>
<div id="addChartDialog" title="Add chart" style="display:none;"> @@ -1453,8 +1526,9 @@ function printMonitor() { </div> </div> </div> - - <div id="loadingLogsDialog" title="<?php echo __('Loading logs'); ?>" style="display:none;"> + + <!-- For generic use --> + <div id="emptyDialog" title="Dialog" style="display:none;"> </div>
<div id="logAnalyseDialog" title="<?php echo __('Log statistics'); ?>" style="display:none;"> diff --git a/sql.php b/sql.php index 94a0b0d..07d54de 100644 --- a/sql.php +++ b/sql.php @@ -939,7 +939,7 @@ $(document).ready(makeProfilingChart); echo '<div style="float: left;">'; echo '<table>' . "\n"; echo ' <tr>' . "\n"; - echo ' <th>' . __('Status') . '</th>' . "\n"; + echo ' <th>' . __('Status') . PMA_showMySQLDocu('general-thread-states','general-thread-states') . '</th>' . "\n"; echo ' <th>' . __('Time') . '</th>' . "\n"; echo ' </tr>' . "\n";
diff --git a/themes/original/css/theme_right.css.php b/themes/original/css/theme_right.css.php index 1e1e2e5..710ec0d 100644 --- a/themes/original/css/theme_right.css.php +++ b/themes/original/css/theme_right.css.php @@ -286,6 +286,19 @@ th.headerSortDown img.sortableIcon { background-position: 0 -1528px; width: 11px
/******************************************************************************/ /* classes */ +.clearfloat { + clear: both; +} + +.floatleft { + float: <?php echo $left; ?>; + margin-<?php echo $right; ?>: 1em; +} + +.paddingtop { + padding-top: 1em; +} + div.tools { border: 1px solid #000000; padding: 0.2em; @@ -1142,10 +1155,6 @@ table#serverstatusconnections { margin-<?php echo $left; ?>: 30px; }
-.clearfloat { - clear: both; -} - table#serverstatusvariables { width: 100%; margin-bottom: 1em; diff --git a/themes/pmahomme/css/theme_left.css.php b/themes/pmahomme/css/theme_left.css.php index 8ed6707..ea50e51 100644 --- a/themes/pmahomme/css/theme_left.css.php +++ b/themes/pmahomme/css/theme_left.css.php @@ -109,6 +109,13 @@ button { /******************************************************************************/ /* specific elements */
+div#frameCollapse { + position:absolute; + top: 50%; + right: 0; + cursor: pointer; +} + div#pmalogo { <?php //better echo $GLOBALS['cfg']['logoBGC']; ?> } diff --git a/themes/pmahomme/css/theme_right.css.php b/themes/pmahomme/css/theme_right.css.php index d2d78e8..582f9a8 100644 --- a/themes/pmahomme/css/theme_right.css.php +++ b/themes/pmahomme/css/theme_right.css.php @@ -466,6 +466,19 @@ th.headerSortDown img.sortableIcon { background-position: 0 0; }
/******************************************************************************/ /* classes */ +.clearfloat { + clear: both; +} + +.floatleft { + float: <?php echo $left; ?>; + margin-<?php echo $right; ?>: 1em; +} + +.paddingtop { + padding-top: 1em; +} + div.tools { /* border: 1px solid #000000; */ padding: 0.2em; @@ -598,7 +611,7 @@ table [class=value] { font-family: <?php echo $GLOBALS['cfg']['FontFamilyFixed']; ?>; } <?php } ?> -.value .attention { +.attention { color: red; font-weight: bold; } @@ -943,6 +956,13 @@ form.login label { /******************************************************************************/ /* specific elements */
+div#frameExpand { + position:absolute; + top: 50%; + left: 0; + cursor: pointer; +} + /* topmenu */ #topmenu a { text-shadow:0px 1px 0px #fff; @@ -1308,7 +1328,7 @@ div#tablestatistics table {
/* serverstatus */
-div#logTable table td.analyzableQuery:hover { +.linkElem:hover { text-decoration: underline; color: #235a81; cursor: pointer; @@ -1359,9 +1379,6 @@ table#serverstatusqueriesdetails th { min-width: 35px; }
-.clearfloat { - clear: both; -} table#serverstatusvariables { width: 100%; margin-bottom: 1em; diff --git a/themes/original/img/bd_spatial.png b/themes/pmahomme/img/frame-collapse.png similarity index 67% copy from themes/original/img/bd_spatial.png copy to themes/pmahomme/img/frame-collapse.png index 40892d0..cee63be 100644 Binary files a/themes/original/img/bd_spatial.png and b/themes/pmahomme/img/frame-collapse.png differ diff --git a/themes/pmahomme/img/frame-expand.png b/themes/pmahomme/img/frame-expand.png new file mode 100644 index 0000000..c99c0d1 Binary files /dev/null and b/themes/pmahomme/img/frame-expand.png differ
hooks/post-receive