diff --git a/libraries/classes/Server/Status/Monitor.php b/libraries/classes/Server/Status/Monitor.php new file mode 100644 index 0000000000..513007c01f --- /dev/null +++ b/libraries/classes/Server/Status/Monitor.php @@ -0,0 +1,827 @@ +'; + $retval .= '
'; + $retval .= '
'; + $retval .= '
'; + + $retval .= ''; + + return $retval; + } + + /** + * Returns html for Analyse Dialog + * + * @return string + */ + public static function getHtmlForAnalyseDialog() + { + $retval = '
'; + $retval .= '

' . __('Selected time range:'); + $retval .= ' - '; + $retval .= ''; + $retval .= '

'; + $retval .= ''; + $retval .= ''; + $retval .= '
'; + $retval .= ''; + $retval .= ''; + $retval .= '

'; + $retval .= __( + 'Choose from which log you want the statistics to be generated from.' + ); + $retval .= '

'; + $retval .= '

'; + $retval .= __('Results are grouped by query text.'); + $retval .= '

'; + $retval .= '
'; + $retval .= '
'; + $retval .= ''; + $retval .= '

'; + $retval .= '
'; + $retval .= '
'; + + return $retval; + } + + /** + * Returns html for Instructions Dialog + * + * @return string + */ + public static function getHtmlForInstructionsDialog() + { + $retval = '
'; + $retval .= __( + '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%.' + ); + + $retval .= '

'; + $retval .= ''; + $retval .= '
'; + $retval .= '
'; + $retval .= '

'; + $retval .= ''; + $retval .= __('Using the monitor:'); + $retval .= '

'; + $retval .= __( + '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.' + ); + $retval .= '

'; + $retval .= __( + '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 occurring SELECT statements to further' + . ' analyze them.' + ); + $retval .= '

'; + $retval .= '

'; + $retval .= Util::getImage('s_attention.png'); + $retval .= ''; + $retval .= __('Please note:'); + $retval .= '
'; + $retval .= __( + '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.' + ); + $retval .= '

'; + $retval .= '
'; + $retval .= '
'; + + return $retval; + } + + /** + * Returns html for addChartDialog + * + * @return string + */ + public static function getHtmlForAddChartDialog() + { + $retval = '
'; + $retval .= '
'; + $retval .= '

'; + $retval .= ''; + $retval .= ''; + $retval .= '
'; + $retval .= ''; + $retval .= '
'; + $retval .= '
'; + $retval .= '
'; + $retval .= '
'; + $retval .= ''; + $retval .= ''; + $retval .= '

'; + $retval .= ''; + $retval .= '
'; + $retval .= ''; + $retval .= ''; + $retval .= ''; + $retval .= ''; + $retval .= '(' . __('KiB') . ', '; + $retval .= '' . __('MiB') . ')'; + $retval .= '
'; + $retval .= ''; + $retval .= ''; + $retval .= ''; + $retval .= ''; + $retval .= ''; + $retval .= '

'; + $retval .= '' . __('Add this series') . ''; + $retval .= ''; + $retval .= ' | ' . __('Clear series') . ''; + $retval .= ''; + $retval .= '

'; + $retval .= __('Series in chart:'); + $retval .= '
'; + $retval .= ''; + $retval .= '' . __('None') . ''; + $retval .= ''; + $retval .= '
'; + $retval .= '
'; + $retval .= '
'; + + return $retval; + } + + /** + * Returns html with Tab Links + * + * @return string + */ + public static function getHtmlForTabLinks() + { + $retval = ''; + + return $retval; + } + + /** + * Returns html with Settings dialog + * + * @return string + */ + public static function getHtmlForSettingsDialog() + { + $retval = '
'; + $retval .= ''; + $retval .= Util::getImage('b_chart.png') . __('Add chart'); + $retval .= ''; + $retval .= ''; + $retval .= Util::getImage('b_tblops.png') + . __('Enable charts dragging'); + $retval .= ''; + $retval .= '
'; + $retval .= '
'; + $retval .= __('Refresh rate') . '
'; + $retval .= ServerStatusData::getHtmlForRefreshList( + 'gridChartRefresh', + 5, + Array(2, 3, 4, 5, 10, 20, 40, 60, 120, 300, 600, 1200) + ); + $retval .= '
'; + $retval .= '
'; + $retval .= '
'; + $retval .= __('Chart columns'); + $retval .= '
'; + $retval .= ''; + $retval .= '
'; + $retval .= '
'; + $retval .= '' . __('Chart arrangement') . ' '; + $retval .= Util::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.' + ) + ); + $retval .= '
'; + $retval .= ''; + $retval .= __('Import'); + $retval .= ''; + $retval .= '  '; + $retval .= ''; + $retval .= __('Export'); + $retval .= ''; + $retval .= '  '; + $retval .= ''; + $retval .= __('Reset to default'); + $retval .= ''; + $retval .= '
'; + $retval .= '
'; + + return $retval; + } + + + /** + * Define some data and links needed on the client side + * + * @param ServerStatusData $ServerStatusData Server status data + * + * @return string + */ + public static function getHtmlForClientSideDataAndLinks($ServerStatusData) + { + /** + * Define some data needed on the client side + */ + $input = ''; + $form = '
'; + $form .= sprintf($input, 'server_time', microtime(true) * 1000); + $form .= sprintf($input, 'server_os', PMA_getSysInfoOs()); + $form .= sprintf($input, 'is_superuser', $GLOBALS['dbi']->isSuperuser()); + $form .= sprintf($input, 'server_db_isLocal', $ServerStatusData->db_isLocal); + $form .= '
'; + /** + * Define some links used on client side + */ + $links = '
'; + $links .= Util::showMySQLDocu('general-thread-states'); + $links .= '
'; + $links .= '
'; + $links .= Util::showMySQLDocu('explain-output'); + $links .= '
'; + + return $form . $links; + } + + /***************************Ajax request function***********************************/ + + /** + * Returns JSon for real-time charting data + * + * @return array + */ + public static function getJsonForChartingData() + { + $ret = json_decode($_REQUEST['requiredData'], true); + $statusVars = array(); + $serverVars = array(); + $sysinfo = $cpuload = $memory = 0; + + /* Accumulate all required variables and data */ + list($serverVars, $statusVars, $ret) = self::getJsonForChartingDataGet( + $ret, $serverVars, $statusVars, $sysinfo, $cpuload, $memory + ); + + // Retrieve all required status variables + if (count($statusVars)) { + $statusVarValues = $GLOBALS['dbi']->fetchResult( + "SHOW GLOBAL STATUS WHERE Variable_name='" + . implode("' OR Variable_name='", $statusVars) . "'", + 0, + 1 + ); + } else { + $statusVarValues = array(); + } + + // Retrieve all required server variables + if (count($serverVars)) { + $serverVarValues = $GLOBALS['dbi']->fetchResult( + "SHOW GLOBAL VARIABLES WHERE Variable_name='" + . implode("' OR Variable_name='", $serverVars) . "'", + 0, + 1 + ); + } else { + $serverVarValues = array(); + } + + // ...and now assign them + $ret = self::getJsonForChartingDataSet($ret, $statusVarValues, $serverVarValues); + + $ret['x'] = microtime(true) * 1000; + return $ret; + } + + /** + * Assign the variables for real-time charting data + * + * @param array $ret Real-time charting data + * @param array $statusVarValues Status variable values + * @param array $serverVarValues Server variable values + * + * @return array + */ + public static function getJsonForChartingDataSet($ret, $statusVarValues, $serverVarValues) + { + foreach ($ret as $chart_id => $chartNodes) { + foreach ($chartNodes as $node_id => $nodeDataPoints) { + foreach ($nodeDataPoints as $point_id => $dataPoint) { + switch ($dataPoint['type']) { + case 'statusvar': + $ret[$chart_id][$node_id][$point_id]['value'] + = $statusVarValues[$dataPoint['name']]; + break; + case 'servervar': + $ret[$chart_id][$node_id][$point_id]['value'] + = $serverVarValues[$dataPoint['name']]; + break; + } + } + } + } + return $ret; + } + + /** + * Get called to get JSON for charting data + * + * @param array $ret Real-time charting data + * @param array $serverVars Server variable values + * @param array $statusVars Status variable values + * @param mixed $sysinfo System info + * @param mixed $cpuload CPU load + * @param mixed $memory Memory + * + * @return array + */ + public static function getJsonForChartingDataGet( + $ret, $serverVars, $statusVars, $sysinfo, $cpuload, $memory + ) { + // For each chart + foreach ($ret as $chart_id => $chartNodes) { + // For each data series + foreach ($chartNodes as $node_id => $nodeDataPoints) { + // For each data point in the series (usually just 1) + foreach ($nodeDataPoints as $point_id => $dataPoint) { + list($serverVars, $statusVars, $ret[$chart_id][$node_id][$point_id]) + = self::getJsonForChartingDataSwitch( + $dataPoint['type'], $dataPoint['name'], $serverVars, + $statusVars, $ret[$chart_id][$node_id][$point_id], + $sysinfo, $cpuload, $memory + ); + } /* foreach */ + } /* foreach */ + } + return array($serverVars, $statusVars, $ret); + } + + /** + * Switch called to get JSON for charting data + * + * @param string $type Type + * @param string $pName Name + * @param array $serverVars Server variable values + * @param array $statusVars Status variable values + * @param array $ret Real-time charting data + * @param mixed $sysinfo System info + * @param mixed $cpuload CPU load + * @param mixed $memory Memory + * + * @return array + */ + public static function getJsonForChartingDataSwitch( + $type, $pName, $serverVars, $statusVars, $ret, + $sysinfo, $cpuload, $memory + ) { + switch ($type) { + /* We only collect the status and server variables here to + * read them all in one query, + * and only afterwards assign them. + * Also do some white list filtering on the names + */ + case 'servervar': + if (!preg_match('/[^a-zA-Z_]+/', $pName)) { + $serverVars[] = $pName; + } + break; + + case 'statusvar': + if (!preg_match('/[^a-zA-Z_]+/', $pName)) { + $statusVars[] = $pName; + } + break; + + case 'proc': + $result = $GLOBALS['dbi']->query('SHOW PROCESSLIST'); + $ret['value'] = $GLOBALS['dbi']->numRows($result); + break; + + case 'cpu': + if (!$sysinfo) { + include_once 'libraries/sysinfo.lib.php'; + $sysinfo = PMA_getSysInfo(); + } + if (!$cpuload) { + $cpuload = $sysinfo->loadavg(); + } + + if (PMA_getSysInfoOs() == 'Linux') { + $ret['idle'] = $cpuload['idle']; + $ret['busy'] = $cpuload['busy']; + } else { + $ret['value'] = $cpuload['loadavg']; + } + + break; + + case 'memory': + if (!$sysinfo) { + include_once 'libraries/sysinfo.lib.php'; + $sysinfo = PMA_getSysInfo(); + } + if (!$memory) { + $memory = $sysinfo->memory(); + } + + $ret['value'] = isset($memory[$pName]) ? $memory[$pName] : 0; + break; + } + + return array($serverVars, $statusVars, $ret); + } + + /** + * Returns JSon for log data with type: slow + * + * @param int $start Unix Time: Start time for query + * @param int $end Unix Time: End time for query + * + * @return array + */ + public static function getJsonForLogDataTypeSlow($start, $end) + { + $query = 'SELECT start_time, user_host, '; + $query .= 'Sec_to_Time(Sum(Time_to_Sec(query_time))) as query_time, '; + $query .= 'Sec_to_Time(Sum(Time_to_Sec(lock_time))) as lock_time, '; + $query .= 'SUM(rows_sent) AS rows_sent, '; + $query .= 'SUM(rows_examined) AS rows_examined, db, sql_text, '; + $query .= 'COUNT(sql_text) AS \'#\' '; + $query .= 'FROM `mysql`.`slow_log` '; + $query .= 'WHERE start_time > FROM_UNIXTIME(' . $start . ') '; + $query .= 'AND start_time < FROM_UNIXTIME(' . $end . ') GROUP BY sql_text'; + + $result = $GLOBALS['dbi']->tryQuery($query); + + $return = array('rows' => array(), 'sum' => array()); + + while ($row = $GLOBALS['dbi']->fetchAssoc($result)) { + $type = mb_strtolower( + mb_substr( + $row['sql_text'], + 0, + mb_strpos($row['sql_text'], ' ') + ) + ); + + switch($type) { + case 'insert': + case 'update': + //Cut off big inserts and updates, but append byte count instead + if (mb_strlen($row['sql_text']) > 220) { + $implode_sql_text = implode( + ' ', + Util::formatByteDown( + mb_strlen($row['sql_text']), 2, 2 + ) + ); + $row['sql_text'] = mb_substr($row['sql_text'], 0, 200) + . '... [' . $implode_sql_text . ']'; + } + 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']); + + $GLOBALS['dbi']->freeResult($result); + return $return; + } + + /** + * Returns JSon for log data with type: general + * + * @param int $start Unix Time: Start time for query + * @param int $end Unix Time: End time for query + * + * @return array + */ + public static function getJsonForLogDataTypeGeneral($start, $end) + { + $limitTypes = ''; + if (isset($_REQUEST['limitTypes']) && $_REQUEST['limitTypes']) { + $limitTypes + = 'AND argument REGEXP \'^(INSERT|SELECT|UPDATE|DELETE)\' '; + } + + $query = 'SELECT TIME(event_time) as event_time, user_host, thread_id, '; + $query .= 'server_id, argument, count(argument) as \'#\' '; + $query .= 'FROM `mysql`.`general_log` '; + $query .= 'WHERE command_type=\'Query\' '; + $query .= 'AND event_time > FROM_UNIXTIME(' . $start . ') '; + $query .= 'AND event_time < FROM_UNIXTIME(' . $end . ') '; + $query .= $limitTypes . 'GROUP by argument'; // HAVING count > 1'; + + $result = $GLOBALS['dbi']->tryQuery($query); + + $return = array('rows' => array(), 'sum' => array()); + $insertTables = array(); + $insertTablesFirst = -1; + $i = 0; + $removeVars = isset($_REQUEST['removeVariables']) + && $_REQUEST['removeVariables']; + + while ($row = $GLOBALS['dbi']->fetchAssoc($result)) { + preg_match('/^(\w+)\s/', $row['argument'], $match); + $type = mb_strtolower($match[1]); + + if (! isset($return['sum'][$type])) { + $return['sum'][$type] = 0; + } + $return['sum'][$type] += $row['#']; + + switch($type) { + /** @noinspection PhpMissingBreakStatementInspection */ + case 'insert': + // Group inserts if selected + if ($removeVars + && preg_match( + '/^INSERT INTO (`|\'|"|)([^\s\\1]+)\\1/i', + $row['argument'], $matches + ) + ) { + $insertTables[$matches[2]]++; + if ($insertTables[$matches[2]] > 1) { + $return['rows'][$insertTablesFirst]['#'] + = $insertTables[$matches[2]]; + + // Add a ... to the end of this query to indicate that + // there's been other queries + $temp = $return['rows'][$insertTablesFirst]['argument']; + $return['rows'][$insertTablesFirst]['argument'] + .= self::getSuspensionPoints( + $temp[strlen($temp) - 1] + ); + + // Group this value, thus do not add to the result list + continue 2; + } else { + $insertTablesFirst = $i; + $insertTables[$matches[2]] += $row['#'] - 1; + } + } + // No break here + + case 'update': + // Cut off big inserts and updates, + // but append byte count therefor + if (mb_strlen($row['argument']) > 220) { + $row['argument'] = mb_substr($row['argument'], 0, 200) + . '... [' + . implode( + ' ', + Util::formatByteDown( + mb_strlen($row['argument']), + 2, + 2 + ) + ) + . ']'; + } + break; + + default: + break; + } + + $return['rows'][] = $row; + $i++; + } + + $return['sum']['TOTAL'] = array_sum($return['sum']); + $return['numRows'] = count($return['rows']); + + $GLOBALS['dbi']->freeResult($result); + + return $return; + } + + /** + * Return suspension points if needed + * + * @param string $lastChar Last char + * + * @return null|string Return suspension points if needed + */ + public static function getSuspensionPoints($lastChar) + { + if ($lastChar != '.') { + return '
...'; + } + + return null; + } + + /** + * Returns JSon for logging vars + * + * @return array + */ + public static function getJsonForLoggingVars() + { + if (isset($_REQUEST['varName']) && isset($_REQUEST['varValue'])) { + $value = $GLOBALS['dbi']->escapeString($_REQUEST['varValue']); + if (! is_numeric($value)) { + $value="'" . $value . "'"; + } + + if (! preg_match("/[^a-zA-Z0-9_]+/", $_REQUEST['varName'])) { + $GLOBALS['dbi']->query( + 'SET GLOBAL ' . $_REQUEST['varName'] . ' = ' . $value + ); + } + + } + + $loggingVars = $GLOBALS['dbi']->fetchResult( + 'SHOW GLOBAL VARIABLES WHERE Variable_name IN' + . ' ("general_log","slow_query_log","long_query_time","log_output")', + 0, + 1 + ); + return $loggingVars; + } + + /** + * Returns JSon for query_analyzer + * + * @return array + */ + public static function getJsonForQueryAnalyzer() + { + $return = array(); + + if (strlen($_REQUEST['database']) > 0) { + $GLOBALS['dbi']->selectDb($_REQUEST['database']); + } + + if ($profiling = Util::profilingSupported()) { + $GLOBALS['dbi']->query('SET PROFILING=1;'); + } + + // Do not cache query + $query = preg_replace( + '/^(\s*SELECT)/i', + '\\1 SQL_NO_CACHE', + $_REQUEST['query'] + ); + + $GLOBALS['dbi']->tryQuery($query); + $return['affectedRows'] = $GLOBALS['cached_affected_rows']; + + $result = $GLOBALS['dbi']->tryQuery('EXPLAIN ' . $query); + while ($row = $GLOBALS['dbi']->fetchAssoc($result)) { + $return['explain'][] = $row; + } + + // In case an error happened + $return['error'] = $GLOBALS['dbi']->getError(); + + $GLOBALS['dbi']->freeResult($result); + + if ($profiling) { + $return['profiling'] = array(); + $result = $GLOBALS['dbi']->tryQuery( + 'SELECT seq,state,duration FROM INFORMATION_SCHEMA.PROFILING' + . ' WHERE QUERY_ID=1 ORDER BY seq' + ); + while ($row = $GLOBALS['dbi']->fetchAssoc($result)) { + $return['profiling'][]= $row; + } + $GLOBALS['dbi']->freeResult($result); + } + return $return; + } +} diff --git a/libraries/server_status_monitor.lib.php b/libraries/server_status_monitor.lib.php deleted file mode 100644 index 64c88637d3..0000000000 --- a/libraries/server_status_monitor.lib.php +++ /dev/null @@ -1,821 +0,0 @@ -'; - $retval .= '
'; - $retval .= '
'; - $retval .= '
'; - - $retval .= ''; - - return $retval; -} - -/** - * Returns html for Analyse Dialog - * - * @return string - */ -function PMA_getHtmlForAnalyseDialog() -{ - $retval = '
'; - $retval .= '

' . __('Selected time range:'); - $retval .= ' - '; - $retval .= ''; - $retval .= '

'; - $retval .= ''; - $retval .= ''; - $retval .= '
'; - $retval .= ''; - $retval .= ''; - $retval .= '

'; - $retval .= __( - 'Choose from which log you want the statistics to be generated from.' - ); - $retval .= '

'; - $retval .= '

'; - $retval .= __('Results are grouped by query text.'); - $retval .= '

'; - $retval .= '
'; - $retval .= '
'; - $retval .= ''; - $retval .= '

'; - $retval .= '
'; - $retval .= '
'; - - return $retval; -} - -/** - * Returns html for Instructions Dialog - * - * @return string - */ -function PMA_getHtmlForInstructionsDialog() -{ - $retval = '
'; - $retval .= __( - '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%.' - ); - - $retval .= '

'; - $retval .= ''; - $retval .= '
'; - $retval .= '
'; - $retval .= '

'; - $retval .= ''; - $retval .= __('Using the monitor:'); - $retval .= '

'; - $retval .= __( - '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.' - ); - $retval .= '

'; - $retval .= __( - '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 occurring SELECT statements to further' - . ' analyze them.' - ); - $retval .= '

'; - $retval .= '

'; - $retval .= PhpMyAdmin\Util::getImage('s_attention.png'); - $retval .= ''; - $retval .= __('Please note:'); - $retval .= '
'; - $retval .= __( - '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.' - ); - $retval .= '

'; - $retval .= '
'; - $retval .= '
'; - - return $retval; -} - -/** - * Returns html for addChartDialog - * - * @return string - */ -function PMA_getHtmlForAddChartDialog() -{ - $retval = '
'; - $retval .= '
'; - $retval .= '

'; - $retval .= ''; - $retval .= ''; - $retval .= '
'; - $retval .= ''; - $retval .= '
'; - $retval .= '
'; - $retval .= '
'; - $retval .= '
'; - $retval .= ''; - $retval .= ''; - $retval .= '

'; - $retval .= ''; - $retval .= '
'; - $retval .= ''; - $retval .= ''; - $retval .= ''; - $retval .= ''; - $retval .= '(' . __('KiB') . ', '; - $retval .= '' . __('MiB') . ')'; - $retval .= '
'; - $retval .= ''; - $retval .= ''; - $retval .= ''; - $retval .= ''; - $retval .= ''; - $retval .= '

'; - $retval .= '' . __('Add this series') . ''; - $retval .= ''; - $retval .= ' | ' . __('Clear series') . ''; - $retval .= ''; - $retval .= '

'; - $retval .= __('Series in chart:'); - $retval .= '
'; - $retval .= ''; - $retval .= '' . __('None') . ''; - $retval .= ''; - $retval .= '
'; - $retval .= '
'; - $retval .= '
'; - - return $retval; -} - -/** - * Returns html with Tab Links - * - * @return string - */ -function PMA_getHtmlForTabLinks() -{ - $retval = ''; - - return $retval; -} - -/** - * Returns html with Settings dialog - * - * @return string - */ -function PMA_getHtmlForSettingsDialog() -{ - $retval = '
'; - $retval .= ''; - $retval .= PhpMyAdmin\Util::getImage('b_chart.png') . __('Add chart'); - $retval .= ''; - $retval .= ''; - $retval .= PhpMyAdmin\Util::getImage('b_tblops.png') - . __('Enable charts dragging'); - $retval .= ''; - $retval .= '
'; - $retval .= '
'; - $retval .= __('Refresh rate') . '
'; - $retval .= ServerStatusData::getHtmlForRefreshList( - 'gridChartRefresh', - 5, - Array(2, 3, 4, 5, 10, 20, 40, 60, 120, 300, 600, 1200) - ); - $retval .= '
'; - $retval .= '
'; - $retval .= '
'; - $retval .= __('Chart columns'); - $retval .= '
'; - $retval .= ''; - $retval .= '
'; - $retval .= '
'; - $retval .= '' . __('Chart arrangement') . ' '; - $retval .= PhpMyAdmin\Util::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.' - ) - ); - $retval .= '
'; - $retval .= ''; - $retval .= __('Import'); - $retval .= ''; - $retval .= '  '; - $retval .= ''; - $retval .= __('Export'); - $retval .= ''; - $retval .= '  '; - $retval .= ''; - $retval .= __('Reset to default'); - $retval .= ''; - $retval .= '
'; - $retval .= '
'; - - return $retval; -} - - -/** - * Define some data and links needed on the client side - * - * @param ServerStatusData $ServerStatusData Server status data - * - * @return string - */ -function PMA_getHtmlForClientSideDataAndLinks($ServerStatusData) -{ - /** - * Define some data needed on the client side - */ - $input = ''; - $form = '
'; - $form .= sprintf($input, 'server_time', microtime(true) * 1000); - $form .= sprintf($input, 'server_os', PMA_getSysInfoOs()); - $form .= sprintf($input, 'is_superuser', $GLOBALS['dbi']->isSuperuser()); - $form .= sprintf($input, 'server_db_isLocal', $ServerStatusData->db_isLocal); - $form .= '
'; - /** - * Define some links used on client side - */ - $links = '
'; - $links .= PhpMyAdmin\Util::showMySQLDocu('general-thread-states'); - $links .= '
'; - $links .= '
'; - $links .= PhpMyAdmin\Util::showMySQLDocu('explain-output'); - $links .= '
'; - - return $form . $links; -} - -/***************************Ajax request function***********************************/ - -/** - * Returns JSon for real-time charting data - * - * @return array - */ -function PMA_getJsonForChartingData() -{ - $ret = json_decode($_REQUEST['requiredData'], true); - $statusVars = array(); - $serverVars = array(); - $sysinfo = $cpuload = $memory = 0; - - /* Accumulate all required variables and data */ - list($serverVars, $statusVars, $ret) = PMA_getJsonForChartingDataGet( - $ret, $serverVars, $statusVars, $sysinfo, $cpuload, $memory - ); - - // Retrieve all required status variables - if (count($statusVars)) { - $statusVarValues = $GLOBALS['dbi']->fetchResult( - "SHOW GLOBAL STATUS WHERE Variable_name='" - . implode("' OR Variable_name='", $statusVars) . "'", - 0, - 1 - ); - } else { - $statusVarValues = array(); - } - - // Retrieve all required server variables - if (count($serverVars)) { - $serverVarValues = $GLOBALS['dbi']->fetchResult( - "SHOW GLOBAL VARIABLES WHERE Variable_name='" - . implode("' OR Variable_name='", $serverVars) . "'", - 0, - 1 - ); - } else { - $serverVarValues = array(); - } - - // ...and now assign them - $ret = PMA_getJsonForChartingDataSet($ret, $statusVarValues, $serverVarValues); - - $ret['x'] = microtime(true) * 1000; - return $ret; -} - -/** - * Assign the variables for real-time charting data - * - * @param array $ret Real-time charting data - * @param array $statusVarValues Status variable values - * @param array $serverVarValues Server variable values - * - * @return array - */ -function PMA_getJsonForChartingDataSet($ret, $statusVarValues, $serverVarValues) -{ - foreach ($ret as $chart_id => $chartNodes) { - foreach ($chartNodes as $node_id => $nodeDataPoints) { - foreach ($nodeDataPoints as $point_id => $dataPoint) { - switch ($dataPoint['type']) { - case 'statusvar': - $ret[$chart_id][$node_id][$point_id]['value'] - = $statusVarValues[$dataPoint['name']]; - break; - case 'servervar': - $ret[$chart_id][$node_id][$point_id]['value'] - = $serverVarValues[$dataPoint['name']]; - break; - } - } - } - } - return $ret; -} - -/** - * Get called to get JSON for charting data - * - * @param array $ret Real-time charting data - * @param array $serverVars Server variable values - * @param array $statusVars Status variable values - * @param mixed $sysinfo System info - * @param mixed $cpuload CPU load - * @param mixed $memory Memory - * - * @return array - */ -function PMA_getJsonForChartingDataGet( - $ret, $serverVars, $statusVars, $sysinfo, $cpuload, $memory -) { - // For each chart - foreach ($ret as $chart_id => $chartNodes) { - // For each data series - foreach ($chartNodes as $node_id => $nodeDataPoints) { - // For each data point in the series (usually just 1) - foreach ($nodeDataPoints as $point_id => $dataPoint) { - list($serverVars, $statusVars, $ret[$chart_id][$node_id][$point_id]) - = PMA_getJsonForChartingDataSwitch( - $dataPoint['type'], $dataPoint['name'], $serverVars, - $statusVars, $ret[$chart_id][$node_id][$point_id], - $sysinfo, $cpuload, $memory - ); - } /* foreach */ - } /* foreach */ - } - return array($serverVars, $statusVars, $ret); -} - -/** - * Switch called to get JSON for charting data - * - * @param string $type Type - * @param string $pName Name - * @param array $serverVars Server variable values - * @param array $statusVars Status variable values - * @param array $ret Real-time charting data - * @param mixed $sysinfo System info - * @param mixed $cpuload CPU load - * @param mixed $memory Memory - * - * @return array - */ -function PMA_getJsonForChartingDataSwitch( - $type, $pName, $serverVars, $statusVars, $ret, - $sysinfo, $cpuload, $memory -) { - switch ($type) { - /* We only collect the status and server variables here to - * read them all in one query, - * and only afterwards assign them. - * Also do some white list filtering on the names - */ - case 'servervar': - if (!preg_match('/[^a-zA-Z_]+/', $pName)) { - $serverVars[] = $pName; - } - break; - - case 'statusvar': - if (!preg_match('/[^a-zA-Z_]+/', $pName)) { - $statusVars[] = $pName; - } - break; - - case 'proc': - $result = $GLOBALS['dbi']->query('SHOW PROCESSLIST'); - $ret['value'] = $GLOBALS['dbi']->numRows($result); - break; - - case 'cpu': - if (!$sysinfo) { - include_once 'libraries/sysinfo.lib.php'; - $sysinfo = PMA_getSysInfo(); - } - if (!$cpuload) { - $cpuload = $sysinfo->loadavg(); - } - - if (PMA_getSysInfoOs() == 'Linux') { - $ret['idle'] = $cpuload['idle']; - $ret['busy'] = $cpuload['busy']; - } else { - $ret['value'] = $cpuload['loadavg']; - } - - break; - - case 'memory': - if (!$sysinfo) { - include_once 'libraries/sysinfo.lib.php'; - $sysinfo = PMA_getSysInfo(); - } - if (!$memory) { - $memory = $sysinfo->memory(); - } - - $ret['value'] = isset($memory[$pName]) ? $memory[$pName] : 0; - break; - } - - return array($serverVars, $statusVars, $ret); -} - -/** - * Returns JSon for log data with type: slow - * - * @param int $start Unix Time: Start time for query - * @param int $end Unix Time: End time for query - * - * @return array - */ -function PMA_getJsonForLogDataTypeSlow($start, $end) -{ - $query = 'SELECT start_time, user_host, '; - $query .= 'Sec_to_Time(Sum(Time_to_Sec(query_time))) as query_time, '; - $query .= 'Sec_to_Time(Sum(Time_to_Sec(lock_time))) as lock_time, '; - $query .= 'SUM(rows_sent) AS rows_sent, '; - $query .= 'SUM(rows_examined) AS rows_examined, db, sql_text, '; - $query .= 'COUNT(sql_text) AS \'#\' '; - $query .= 'FROM `mysql`.`slow_log` '; - $query .= 'WHERE start_time > FROM_UNIXTIME(' . $start . ') '; - $query .= 'AND start_time < FROM_UNIXTIME(' . $end . ') GROUP BY sql_text'; - - $result = $GLOBALS['dbi']->tryQuery($query); - - $return = array('rows' => array(), 'sum' => array()); - - while ($row = $GLOBALS['dbi']->fetchAssoc($result)) { - $type = mb_strtolower( - mb_substr( - $row['sql_text'], - 0, - mb_strpos($row['sql_text'], ' ') - ) - ); - - switch($type) { - case 'insert': - case 'update': - //Cut off big inserts and updates, but append byte count instead - if (mb_strlen($row['sql_text']) > 220) { - $implode_sql_text = implode( - ' ', - PhpMyAdmin\Util::formatByteDown( - mb_strlen($row['sql_text']), 2, 2 - ) - ); - $row['sql_text'] = mb_substr($row['sql_text'], 0, 200) - . '... [' . $implode_sql_text . ']'; - } - 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']); - - $GLOBALS['dbi']->freeResult($result); - return $return; -} - -/** - * Returns JSon for log data with type: general - * - * @param int $start Unix Time: Start time for query - * @param int $end Unix Time: End time for query - * - * @return array - */ -function PMA_getJsonForLogDataTypeGeneral($start, $end) -{ - $limitTypes = ''; - if (isset($_REQUEST['limitTypes']) && $_REQUEST['limitTypes']) { - $limitTypes - = 'AND argument REGEXP \'^(INSERT|SELECT|UPDATE|DELETE)\' '; - } - - $query = 'SELECT TIME(event_time) as event_time, user_host, thread_id, '; - $query .= 'server_id, argument, count(argument) as \'#\' '; - $query .= 'FROM `mysql`.`general_log` '; - $query .= 'WHERE command_type=\'Query\' '; - $query .= 'AND event_time > FROM_UNIXTIME(' . $start . ') '; - $query .= 'AND event_time < FROM_UNIXTIME(' . $end . ') '; - $query .= $limitTypes . 'GROUP by argument'; // HAVING count > 1'; - - $result = $GLOBALS['dbi']->tryQuery($query); - - $return = array('rows' => array(), 'sum' => array()); - $insertTables = array(); - $insertTablesFirst = -1; - $i = 0; - $removeVars = isset($_REQUEST['removeVariables']) - && $_REQUEST['removeVariables']; - - while ($row = $GLOBALS['dbi']->fetchAssoc($result)) { - preg_match('/^(\w+)\s/', $row['argument'], $match); - $type = mb_strtolower($match[1]); - - if (! isset($return['sum'][$type])) { - $return['sum'][$type] = 0; - } - $return['sum'][$type] += $row['#']; - - switch($type) { - /** @noinspection PhpMissingBreakStatementInspection */ - case 'insert': - // Group inserts if selected - if ($removeVars - && preg_match( - '/^INSERT INTO (`|\'|"|)([^\s\\1]+)\\1/i', - $row['argument'], $matches - ) - ) { - $insertTables[$matches[2]]++; - if ($insertTables[$matches[2]] > 1) { - $return['rows'][$insertTablesFirst]['#'] - = $insertTables[$matches[2]]; - - // Add a ... to the end of this query to indicate that - // there's been other queries - $temp = $return['rows'][$insertTablesFirst]['argument']; - $return['rows'][$insertTablesFirst]['argument'] - .= PMA_getSuspensionPoints( - $temp[strlen($temp) - 1] - ); - - // Group this value, thus do not add to the result list - continue 2; - } else { - $insertTablesFirst = $i; - $insertTables[$matches[2]] += $row['#'] - 1; - } - } - // No break here - - case 'update': - // Cut off big inserts and updates, - // but append byte count therefor - if (mb_strlen($row['argument']) > 220) { - $row['argument'] = mb_substr($row['argument'], 0, 200) - . '... [' - . implode( - ' ', - PhpMyAdmin\Util::formatByteDown( - mb_strlen($row['argument']), - 2, - 2 - ) - ) - . ']'; - } - break; - - default: - break; - } - - $return['rows'][] = $row; - $i++; - } - - $return['sum']['TOTAL'] = array_sum($return['sum']); - $return['numRows'] = count($return['rows']); - - $GLOBALS['dbi']->freeResult($result); - - return $return; -} - -/** - * Return suspension points if needed - * - * @param string $lastChar Last char - * - * @return null|string Return suspension points if needed - */ -function PMA_getSuspensionPoints($lastChar) -{ - if ($lastChar != '.') { - return '
...'; - } - - return null; -} -/** - * Returns JSon for logging vars - * - * @return array - */ -function PMA_getJsonForLoggingVars() -{ - if (isset($_REQUEST['varName']) && isset($_REQUEST['varValue'])) { - $value = $GLOBALS['dbi']->escapeString($_REQUEST['varValue']); - if (! is_numeric($value)) { - $value="'" . $value . "'"; - } - - if (! preg_match("/[^a-zA-Z0-9_]+/", $_REQUEST['varName'])) { - $GLOBALS['dbi']->query( - 'SET GLOBAL ' . $_REQUEST['varName'] . ' = ' . $value - ); - } - - } - - $loggingVars = $GLOBALS['dbi']->fetchResult( - 'SHOW GLOBAL VARIABLES WHERE Variable_name IN' - . ' ("general_log","slow_query_log","long_query_time","log_output")', - 0, - 1 - ); - return $loggingVars; -} - -/** - * Returns JSon for query_analyzer - * - * @return array - */ -function PMA_getJsonForQueryAnalyzer() -{ - $return = array(); - - if (strlen($_REQUEST['database']) > 0) { - $GLOBALS['dbi']->selectDb($_REQUEST['database']); - } - - if ($profiling = PhpMyAdmin\Util::profilingSupported()) { - $GLOBALS['dbi']->query('SET PROFILING=1;'); - } - - // Do not cache query - $query = preg_replace( - '/^(\s*SELECT)/i', - '\\1 SQL_NO_CACHE', - $_REQUEST['query'] - ); - - $GLOBALS['dbi']->tryQuery($query); - $return['affectedRows'] = $GLOBALS['cached_affected_rows']; - - $result = $GLOBALS['dbi']->tryQuery('EXPLAIN ' . $query); - while ($row = $GLOBALS['dbi']->fetchAssoc($result)) { - $return['explain'][] = $row; - } - - // In case an error happened - $return['error'] = $GLOBALS['dbi']->getError(); - - $GLOBALS['dbi']->freeResult($result); - - if ($profiling) { - $return['profiling'] = array(); - $result = $GLOBALS['dbi']->tryQuery( - 'SELECT seq,state,duration FROM INFORMATION_SCHEMA.PROFILING' - . ' WHERE QUERY_ID=1 ORDER BY seq' - ); - while ($row = $GLOBALS['dbi']->fetchAssoc($result)) { - $return['profiling'][]= $row; - } - $GLOBALS['dbi']->freeResult($result); - } - return $return; -} diff --git a/server_status_monitor.php b/server_status_monitor.php index 2789a0a85a..55b2f7c265 100644 --- a/server_status_monitor.php +++ b/server_status_monitor.php @@ -6,12 +6,12 @@ * @package PhpMyAdmin */ +use PhpMyAdmin\Server\Status\Monitor; use PhpMyAdmin\ServerStatusData; use PhpMyAdmin\Response; require_once 'libraries/common.inc.php'; require_once 'libraries/server_common.inc.php'; -require_once 'libraries/server_status_monitor.lib.php'; require_once 'libraries/replication.inc.php'; require_once 'libraries/replication_gui.lib.php'; @@ -28,7 +28,7 @@ if ($response->isAjax()) { if (isset($_REQUEST['chart_data'])) { switch($_REQUEST['type']) { case 'chartgrid': // Data for the monitor - $ret = PMA_getJsonForChartingData(); + $ret = Monitor::getJsonForChartingData(); $response->addJSON('message', $ret); exit; } @@ -40,26 +40,26 @@ if ($response->isAjax()) { $end = intval($_REQUEST['time_end']); if ($_REQUEST['type'] == 'slow') { - $return = PMA_getJsonForLogDataTypeSlow($start, $end); + $return = Monitor::getJsonForLogDataTypeSlow($start, $end); $response->addJSON('message', $return); exit; } if ($_REQUEST['type'] == 'general') { - $return = PMA_getJsonForLogDataTypeGeneral($start, $end); + $return = Monitor::getJsonForLogDataTypeGeneral($start, $end); $response->addJSON('message', $return); exit; } } if (isset($_REQUEST['logging_vars'])) { - $loggingVars = PMA_getJsonForLoggingVars(); + $loggingVars = Monitor::getJsonForLoggingVars(); $response->addJSON('message', $loggingVars); exit; } if (isset($_REQUEST['query_analyzer'])) { - $return = PMA_getJsonForQueryAnalyzer(); + $return = Monitor::getJsonForQueryAnalyzer(); $response->addJSON('message', $return); exit; } @@ -98,7 +98,7 @@ $ServerStatusData = new ServerStatusData(); */ $response->addHTML('
'); $response->addHTML($ServerStatusData->getMenuHtml()); -$response->addHTML(PMA_getHtmlForMonitor($ServerStatusData)); -$response->addHTML(PMA_getHtmlForClientSideDataAndLinks($ServerStatusData)); +$response->addHTML(Monitor::getHtmlForMonitor($ServerStatusData)); +$response->addHTML(Monitor::getHtmlForClientSideDataAndLinks($ServerStatusData)); $response->addHTML('
'); exit; diff --git a/test/libraries/PMA_server_status_monitor_test.php b/test/classes/Server/Status/MonitorTest.php similarity index 89% rename from test/libraries/PMA_server_status_monitor_test.php rename to test/classes/Server/Status/MonitorTest.php index fc55bb61bd..9f78085751 100644 --- a/test/libraries/PMA_server_status_monitor_test.php +++ b/test/classes/Server/Status/MonitorTest.php @@ -1,25 +1,23 @@ ServerStatusData); + $html = Monitor::getHtmlForMonitor($this->ServerStatusData); - //validate 1: PMA_getHtmlForTabLinks + //validate 1: Monitor::getHtmlForTabLinks $this->assertContains( '