Monitor.php 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829
  1. <?php
  2. /* vim: set expandtab sw=4 ts=4 sts=4: */
  3. /**
  4. * functions for displaying server status sub item: monitor
  5. *
  6. * @usedby server_status_monitor.php
  7. *
  8. * @package PhpMyAdmin
  9. */
  10. namespace PhpMyAdmin\Server\Status;
  11. use PhpMyAdmin\Sanitize;
  12. use PhpMyAdmin\Server\Status\Data;
  13. use PhpMyAdmin\SysInfo;
  14. use PhpMyAdmin\Util;
  15. /**
  16. * functions for displaying server status sub item: monitor
  17. *
  18. * @package PhpMyAdmin
  19. */
  20. class Monitor
  21. {
  22. /**
  23. * Prints html with monitor
  24. *
  25. * @param Data $serverStatusData Server status data
  26. *
  27. * @return string
  28. */
  29. public static function getHtmlForMonitor(Data $serverStatusData)
  30. {
  31. $retval = self::getHtmlForTabLinks();
  32. $retval .= self::getHtmlForSettingsDialog();
  33. $retval .= self::getHtmlForInstructionsDialog();
  34. $retval .= self::getHtmlForAddChartDialog();
  35. $retval .= self::getHtmlForAnalyseDialog();
  36. $retval .= '<table class="clearfloat tdblock" id="chartGrid"></table>';
  37. $retval .= '<div id="logTable">';
  38. $retval .= '<br/>';
  39. $retval .= '</div>';
  40. $retval .= '<script type="text/javascript">';
  41. $retval .= 'variableNames = [ ';
  42. $i = 0;
  43. foreach ($serverStatusData->status as $name=>$value) {
  44. if (is_numeric($value)) {
  45. if ($i++ > 0) {
  46. $retval .= ", ";
  47. }
  48. $retval .= Sanitize::formatJsVal($name);
  49. }
  50. }
  51. $retval .= '];';
  52. $retval .= '</script>';
  53. return $retval;
  54. }
  55. /**
  56. * Returns html for Analyse Dialog
  57. *
  58. * @return string
  59. */
  60. public static function getHtmlForAnalyseDialog()
  61. {
  62. $retval = '<div id="logAnalyseDialog" title="';
  63. $retval .= __('Log statistics') . '" class="hide">';
  64. $retval .= '<p>' . __('Selected time range:');
  65. $retval .= '<input type="text" name="dateStart"'
  66. . ' class="datetimefield" value="" /> - ';
  67. $retval .= '<input type="text" name="dateEnd" class="datetimefield" value="" />';
  68. $retval .= '</p>';
  69. $retval .= '<input type="checkbox" id="limitTypes"'
  70. . ' value="1" checked="checked" />';
  71. $retval .= '<label for="limitTypes">';
  72. $retval .= __('Only retrieve SELECT,INSERT,UPDATE and DELETE Statements');
  73. $retval .= '</label>';
  74. $retval .= '<br/>';
  75. $retval .= '<input type="checkbox" id="removeVariables"'
  76. . ' value="1" checked="checked" />';
  77. $retval .= '<label for="removeVariables">';
  78. $retval .= __('Remove variable data in INSERT statements for better grouping');
  79. $retval .= '</label>';
  80. $retval .= '<p>';
  81. $retval .= __(
  82. 'Choose from which log you want the statistics to be generated from.'
  83. );
  84. $retval .= '</p>';
  85. $retval .= '<p>';
  86. $retval .= __('Results are grouped by query text.');
  87. $retval .= '</p>';
  88. $retval .= '</div>';
  89. $retval .= '<div id="queryAnalyzerDialog" title="';
  90. $retval .= __('Query analyzer') . '" class="hide">';
  91. $retval .= '<textarea id="sqlquery"> </textarea>';
  92. $retval .= '<p></p>';
  93. $retval .= '<div class="placeHolder"></div>';
  94. $retval .= '</div>';
  95. return $retval;
  96. }
  97. /**
  98. * Returns html for Instructions Dialog
  99. *
  100. * @return string
  101. */
  102. public static function getHtmlForInstructionsDialog()
  103. {
  104. $retval = '<div id="monitorInstructionsDialog" title="';
  105. $retval .= __('Monitor Instructions') . '" class="hide">';
  106. $retval .= __(
  107. 'The phpMyAdmin Monitor can assist you in optimizing the server'
  108. . ' configuration and track down time intensive queries. For the latter you'
  109. . ' will need to set log_output to \'TABLE\' and have either the'
  110. . ' slow_query_log or general_log enabled. Note however, that the'
  111. . ' general_log produces a lot of data and increases server load'
  112. . ' by up to 15%.'
  113. );
  114. $retval .= '<p></p>';
  115. $retval .= '<img class="ajaxIcon" src="';
  116. $retval .= $GLOBALS['pmaThemeImage'] . 'ajax_clock_small.gif"';
  117. $retval .= ' alt="' . __('Loading…') . '" />';
  118. $retval .= '<div class="ajaxContent"></div>';
  119. $retval .= '<div class="monitorUse hide">';
  120. $retval .= '<p></p>';
  121. $retval .= '<strong>';
  122. $retval .= __('Using the monitor:');
  123. $retval .= '</strong><p>';
  124. $retval .= __(
  125. 'Your browser will refresh all displayed charts in a regular interval.'
  126. . ' You may add charts and change the refresh rate under \'Settings\','
  127. . ' or remove any chart using the cog icon on each respective chart.'
  128. );
  129. $retval .= '</p><p>';
  130. $retval .= __(
  131. 'To display queries from the logs, select the relevant time span on any'
  132. . ' chart by holding down the left mouse button and panning over the'
  133. . ' chart. Once confirmed, this will load a table of grouped queries,'
  134. . ' there you may click on any occurring SELECT statements to further'
  135. . ' analyze them.'
  136. );
  137. $retval .= '</p>';
  138. $retval .= '<p>';
  139. $retval .= Util::getImage('s_attention');
  140. $retval .= '<strong>';
  141. $retval .= __('Please note:');
  142. $retval .= '</strong><br />';
  143. $retval .= __(
  144. 'Enabling the general_log may increase the server load by'
  145. . ' 5-15%. Also be aware that generating statistics from the logs is a'
  146. . ' load intensive task, so it is advisable to select only a small time'
  147. . ' span and to disable the general_log and empty its table once'
  148. . ' monitoring is not required any more.'
  149. );
  150. $retval .= '</p>';
  151. $retval .= '</div>';
  152. $retval .= '</div>';
  153. return $retval;
  154. }
  155. /**
  156. * Returns html for addChartDialog
  157. *
  158. * @return string
  159. */
  160. public static function getHtmlForAddChartDialog()
  161. {
  162. $retval = '<div id="addChartDialog" title="'
  163. . __('Add chart') . '" class="hide">';
  164. $retval .= '<div id="tabGridVariables">';
  165. $retval .= '<p><input type="text" name="chartTitle" value="'
  166. . __('Chart Title') . '" /></p>';
  167. $retval .= '<input type="radio" name="chartType"'
  168. . ' value="preset" id="chartPreset" />';
  169. $retval .= '<label for="chartPreset">' . __('Preset chart') . '</label>';
  170. $retval .= '<select name="presetCharts"></select><br/>';
  171. $retval .= '<input type="radio" name="chartType" value="variable" '
  172. . 'id="chartStatusVar" checked="checked" />';
  173. $retval .= '<label for="chartStatusVar">';
  174. $retval .= __('Status variable(s)');
  175. $retval .= '</label><br/>';
  176. $retval .= '<div id="chartVariableSettings">';
  177. $retval .= '<label for="chartSeries">' . __('Select series:') . '</label><br />';
  178. $retval .= '<select id="chartSeries" name="varChartList" size="1">';
  179. $retval .= '<option>' . __('Commonly monitored') . '</option>';
  180. $retval .= '<option>Processes</option>';
  181. $retval .= '<option>Questions</option>';
  182. $retval .= '<option>Connections</option>';
  183. $retval .= '<option>Bytes_sent</option>';
  184. $retval .= '<option>Bytes_received</option>';
  185. $retval .= '<option>Threads_connected</option>';
  186. $retval .= '<option>Created_tmp_disk_tables</option>';
  187. $retval .= '<option>Handler_read_first</option>';
  188. $retval .= '<option>Innodb_buffer_pool_wait_free</option>';
  189. $retval .= '<option>Key_reads</option>';
  190. $retval .= '<option>Open_tables</option>';
  191. $retval .= '<option>Select_full_join</option>';
  192. $retval .= '<option>Slow_queries</option>';
  193. $retval .= '</select><br />';
  194. $retval .= '<label for="variableInput">';
  195. $retval .= __('or type variable name:');
  196. $retval .= ' </label>';
  197. $retval .= '<input type="text" name="variableInput" id="variableInput" />';
  198. $retval .= '<p></p>';
  199. $retval .= '<input type="checkbox" name="differentialValue"'
  200. . ' id="differentialValue" value="differential" checked="checked" />';
  201. $retval .= '<label for="differentialValue">';
  202. $retval .= __('Display as differential value');
  203. $retval .= '</label><br />';
  204. $retval .= '<input type="checkbox" id="useDivisor"'
  205. . ' name="useDivisor" value="1" />';
  206. $retval .= '<label for="useDivisor">' . __('Apply a divisor') . '</label>';
  207. $retval .= '<span class="divisorInput hide">';
  208. $retval .= '<input type="text" name="valueDivisor" size="4" value="1" />';
  209. $retval .= '(<a href="#kibDivisor">' . __('KiB') . '</a>, ';
  210. $retval .= '<a href="#mibDivisor">' . __('MiB') . '</a>)';
  211. $retval .= '</span><br />';
  212. $retval .= '<input type="checkbox" id="useUnit" name="useUnit" value="1" />';
  213. $retval .= '<label for="useUnit">';
  214. $retval .= __('Append unit to data values');
  215. $retval .= '</label>';
  216. $retval .= '<span class="unitInput hide">';
  217. $retval .= '<input type="text" name="valueUnit" size="4" value="" />';
  218. $retval .= '</span>';
  219. $retval .= '<p>';
  220. $retval .= '<a href="#submitAddSeries"><b>' . __('Add this series') . '</b></a>';
  221. $retval .= '<span id="clearSeriesLink" class="hide">';
  222. $retval .= ' | <a href="#submitClearSeries">' . __('Clear series') . '</a>';
  223. $retval .= '</span>';
  224. $retval .= '</p>';
  225. $retval .= __('Series in chart:');
  226. $retval .= '<br/>';
  227. $retval .= '<span id="seriesPreview">';
  228. $retval .= '<i>' . __('None') . '</i>';
  229. $retval .= '</span>';
  230. $retval .= '</div>';
  231. $retval .= '</div>';
  232. $retval .= '</div>';
  233. return $retval;
  234. }
  235. /**
  236. * Returns html with Tab Links
  237. *
  238. * @return string
  239. */
  240. public static function getHtmlForTabLinks()
  241. {
  242. $retval = '<div class="tabLinks">';
  243. $retval .= '<a href="#pauseCharts">';
  244. $retval .= Util::getImage('play') . __('Start Monitor');
  245. $retval .= '</a>';
  246. $retval .= '<a href="#settingsPopup" class="popupLink">';
  247. $retval .= Util::getImage('s_cog') . __('Settings');
  248. $retval .= '</a>';
  249. $retval .= '<a href="#monitorInstructionsDialog">';
  250. $retval .= Util::getImage('b_help') . __('Instructions/Setup');
  251. $retval .= '<a href="#endChartEditMode" class="hide">';
  252. $retval .= Util::getImage('s_okay');
  253. $retval .= __('Done dragging (rearranging) charts');
  254. $retval .= '</a>';
  255. $retval .= '</div>';
  256. return $retval;
  257. }
  258. /**
  259. * Returns html with Settings dialog
  260. *
  261. * @return string
  262. */
  263. public static function getHtmlForSettingsDialog()
  264. {
  265. $retval = '<div class="popupContent settingsPopup">';
  266. $retval .= '<a href="#addNewChart">';
  267. $retval .= Util::getImage('b_chart') . __('Add chart');
  268. $retval .= '</a>';
  269. $retval .= '<a href="#rearrangeCharts">';
  270. $retval .= Util::getImage('b_tblops')
  271. . __('Enable charts dragging');
  272. $retval .= '</a>';
  273. $retval .= '<div class="clearfloat paddingtop"></div>';
  274. $retval .= '<div class="floatleft">';
  275. $retval .= __('Refresh rate') . '<br />';
  276. $retval .= Data::getHtmlForRefreshList(
  277. 'gridChartRefresh',
  278. 5,
  279. Array(2, 3, 4, 5, 10, 20, 40, 60, 120, 300, 600, 1200)
  280. );
  281. $retval .= '<br />';
  282. $retval .= '</div>';
  283. $retval .= '<div class="floatleft">';
  284. $retval .= __('Chart columns');
  285. $retval .= '<br />';
  286. $retval .= '<select name="chartColumns">';
  287. $retval .= '<option>1</option>';
  288. $retval .= '<option>2</option>';
  289. $retval .= '<option>3</option>';
  290. $retval .= '<option>4</option>';
  291. $retval .= '<option>5</option>';
  292. $retval .= '<option>6</option>';
  293. $retval .= '</select>';
  294. $retval .= '</div>';
  295. $retval .= '<div class="clearfloat paddingtop">';
  296. $retval .= '<b>' . __('Chart arrangement') . '</b> ';
  297. $retval .= Util::showHint(
  298. __(
  299. 'The arrangement of the charts is stored to the browsers local storage. '
  300. . 'You may want to export it if you have a complicated set up.'
  301. )
  302. );
  303. $retval .= '<br/>';
  304. $retval .= '<a class="ajax" href="#importMonitorConfig">';
  305. $retval .= __('Import');
  306. $retval .= '</a>';
  307. $retval .= '&nbsp;&nbsp;';
  308. $retval .= '<a class="disableAjax" href="#exportMonitorConfig">';
  309. $retval .= __('Export');
  310. $retval .= '</a>';
  311. $retval .= '&nbsp;&nbsp;';
  312. $retval .= '<a href="#clearMonitorConfig">';
  313. $retval .= __('Reset to default');
  314. $retval .= '</a>';
  315. $retval .= '</div>';
  316. $retval .= '</div>';
  317. return $retval;
  318. }
  319. /**
  320. * Define some data and links needed on the client side
  321. *
  322. * @param Data $serverStatusData Server status data
  323. *
  324. * @return string
  325. */
  326. public static function getHtmlForClientSideDataAndLinks(Data $serverStatusData)
  327. {
  328. /**
  329. * Define some data needed on the client side
  330. */
  331. $input = '<input type="hidden" name="%s" value="%s" />';
  332. $form = '<form id="js_data" class="hide">';
  333. $form .= sprintf($input, 'server_time', microtime(true) * 1000);
  334. $form .= sprintf($input, 'server_os', SysInfo::getOs());
  335. $form .= sprintf($input, 'is_superuser', $GLOBALS['dbi']->isSuperuser());
  336. $form .= sprintf($input, 'server_db_isLocal', $serverStatusData->db_isLocal);
  337. $form .= '</form>';
  338. /**
  339. * Define some links used on client side
  340. */
  341. $links = '<div id="profiling_docu" class="hide">';
  342. $links .= Util::showMySQLDocu('general-thread-states');
  343. $links .= '</div>';
  344. $links .= '<div id="explain_docu" class="hide">';
  345. $links .= Util::showMySQLDocu('explain-output');
  346. $links .= '</div>';
  347. return $form . $links;
  348. }
  349. /***************************Ajax request function***********************************/
  350. /**
  351. * Returns JSon for real-time charting data
  352. *
  353. * @return array
  354. */
  355. public static function getJsonForChartingData()
  356. {
  357. $ret = json_decode($_POST['requiredData'], true);
  358. $statusVars = array();
  359. $serverVars = array();
  360. $sysinfo = $cpuload = $memory = 0;
  361. /* Accumulate all required variables and data */
  362. list($serverVars, $statusVars, $ret) = self::getJsonForChartingDataGet(
  363. $ret, $serverVars, $statusVars, $sysinfo, $cpuload, $memory
  364. );
  365. // Retrieve all required status variables
  366. if (count($statusVars)) {
  367. $statusVarValues = $GLOBALS['dbi']->fetchResult(
  368. "SHOW GLOBAL STATUS WHERE Variable_name='"
  369. . implode("' OR Variable_name='", $statusVars) . "'",
  370. 0,
  371. 1
  372. );
  373. } else {
  374. $statusVarValues = array();
  375. }
  376. // Retrieve all required server variables
  377. if (count($serverVars)) {
  378. $serverVarValues = $GLOBALS['dbi']->fetchResult(
  379. "SHOW GLOBAL VARIABLES WHERE Variable_name='"
  380. . implode("' OR Variable_name='", $serverVars) . "'",
  381. 0,
  382. 1
  383. );
  384. } else {
  385. $serverVarValues = array();
  386. }
  387. // ...and now assign them
  388. $ret = self::getJsonForChartingDataSet($ret, $statusVarValues, $serverVarValues);
  389. $ret['x'] = microtime(true) * 1000;
  390. return $ret;
  391. }
  392. /**
  393. * Assign the variables for real-time charting data
  394. *
  395. * @param array $ret Real-time charting data
  396. * @param array $statusVarValues Status variable values
  397. * @param array $serverVarValues Server variable values
  398. *
  399. * @return array
  400. */
  401. public static function getJsonForChartingDataSet(array $ret, array $statusVarValues, array $serverVarValues)
  402. {
  403. foreach ($ret as $chart_id => $chartNodes) {
  404. foreach ($chartNodes as $node_id => $nodeDataPoints) {
  405. foreach ($nodeDataPoints as $point_id => $dataPoint) {
  406. switch ($dataPoint['type']) {
  407. case 'statusvar':
  408. $ret[$chart_id][$node_id][$point_id]['value']
  409. = $statusVarValues[$dataPoint['name']];
  410. break;
  411. case 'servervar':
  412. $ret[$chart_id][$node_id][$point_id]['value']
  413. = $serverVarValues[$dataPoint['name']];
  414. break;
  415. }
  416. }
  417. }
  418. }
  419. return $ret;
  420. }
  421. /**
  422. * Get called to get JSON for charting data
  423. *
  424. * @param array $ret Real-time charting data
  425. * @param array $serverVars Server variable values
  426. * @param array $statusVars Status variable values
  427. * @param mixed $sysinfo System info
  428. * @param mixed $cpuload CPU load
  429. * @param mixed $memory Memory
  430. *
  431. * @return array
  432. */
  433. public static function getJsonForChartingDataGet(
  434. array $ret, array $serverVars, array $statusVars, $sysinfo, $cpuload, $memory
  435. ) {
  436. // For each chart
  437. foreach ($ret as $chart_id => $chartNodes) {
  438. // For each data series
  439. foreach ($chartNodes as $node_id => $nodeDataPoints) {
  440. // For each data point in the series (usually just 1)
  441. foreach ($nodeDataPoints as $point_id => $dataPoint) {
  442. list($serverVars, $statusVars, $ret[$chart_id][$node_id][$point_id])
  443. = self::getJsonForChartingDataSwitch(
  444. $dataPoint['type'], $dataPoint['name'], $serverVars,
  445. $statusVars, $ret[$chart_id][$node_id][$point_id],
  446. $sysinfo, $cpuload, $memory
  447. );
  448. } /* foreach */
  449. } /* foreach */
  450. }
  451. return array($serverVars, $statusVars, $ret);
  452. }
  453. /**
  454. * Switch called to get JSON for charting data
  455. *
  456. * @param string $type Type
  457. * @param string $pName Name
  458. * @param array $serverVars Server variable values
  459. * @param array $statusVars Status variable values
  460. * @param array $ret Real-time charting data
  461. * @param mixed $sysinfo System info
  462. * @param mixed $cpuload CPU load
  463. * @param mixed $memory Memory
  464. *
  465. * @return array
  466. */
  467. public static function getJsonForChartingDataSwitch(
  468. $type, $pName, array $serverVars, array $statusVars, array $ret,
  469. $sysinfo, $cpuload, $memory
  470. ) {
  471. switch ($type) {
  472. /* We only collect the status and server variables here to
  473. * read them all in one query,
  474. * and only afterwards assign them.
  475. * Also do some white list filtering on the names
  476. */
  477. case 'servervar':
  478. if (!preg_match('/[^a-zA-Z_]+/', $pName)) {
  479. $serverVars[] = $pName;
  480. }
  481. break;
  482. case 'statusvar':
  483. if (!preg_match('/[^a-zA-Z_]+/', $pName)) {
  484. $statusVars[] = $pName;
  485. }
  486. break;
  487. case 'proc':
  488. $result = $GLOBALS['dbi']->query('SHOW PROCESSLIST');
  489. $ret['value'] = $GLOBALS['dbi']->numRows($result);
  490. break;
  491. case 'cpu':
  492. if (!$sysinfo) {
  493. $sysinfo = SysInfo::get();
  494. }
  495. if (!$cpuload) {
  496. $cpuload = $sysinfo->loadavg();
  497. }
  498. if (SysInfo::getOs() == 'Linux') {
  499. $ret['idle'] = $cpuload['idle'];
  500. $ret['busy'] = $cpuload['busy'];
  501. } else {
  502. $ret['value'] = $cpuload['loadavg'];
  503. }
  504. break;
  505. case 'memory':
  506. if (!$sysinfo) {
  507. $sysinfo = SysInfo::get();
  508. }
  509. if (!$memory) {
  510. $memory = $sysinfo->memory();
  511. }
  512. $ret['value'] = isset($memory[$pName]) ? $memory[$pName] : 0;
  513. break;
  514. }
  515. return array($serverVars, $statusVars, $ret);
  516. }
  517. /**
  518. * Returns JSon for log data with type: slow
  519. *
  520. * @param int $start Unix Time: Start time for query
  521. * @param int $end Unix Time: End time for query
  522. *
  523. * @return array
  524. */
  525. public static function getJsonForLogDataTypeSlow($start, $end)
  526. {
  527. $query = 'SELECT start_time, user_host, ';
  528. $query .= 'Sec_to_Time(Sum(Time_to_Sec(query_time))) as query_time, ';
  529. $query .= 'Sec_to_Time(Sum(Time_to_Sec(lock_time))) as lock_time, ';
  530. $query .= 'SUM(rows_sent) AS rows_sent, ';
  531. $query .= 'SUM(rows_examined) AS rows_examined, db, sql_text, ';
  532. $query .= 'COUNT(sql_text) AS \'#\' ';
  533. $query .= 'FROM `mysql`.`slow_log` ';
  534. $query .= 'WHERE start_time > FROM_UNIXTIME(' . $start . ') ';
  535. $query .= 'AND start_time < FROM_UNIXTIME(' . $end . ') GROUP BY sql_text';
  536. $result = $GLOBALS['dbi']->tryQuery($query);
  537. $return = array('rows' => array(), 'sum' => array());
  538. while ($row = $GLOBALS['dbi']->fetchAssoc($result)) {
  539. $type = mb_strtolower(
  540. mb_substr(
  541. $row['sql_text'],
  542. 0,
  543. mb_strpos($row['sql_text'], ' ')
  544. )
  545. );
  546. switch($type) {
  547. case 'insert':
  548. case 'update':
  549. //Cut off big inserts and updates, but append byte count instead
  550. if (mb_strlen($row['sql_text']) > 220) {
  551. $implode_sql_text = implode(
  552. ' ',
  553. Util::formatByteDown(
  554. mb_strlen($row['sql_text']), 2, 2
  555. )
  556. );
  557. $row['sql_text'] = mb_substr($row['sql_text'], 0, 200)
  558. . '... [' . $implode_sql_text . ']';
  559. }
  560. break;
  561. default:
  562. break;
  563. }
  564. if (! isset($return['sum'][$type])) {
  565. $return['sum'][$type] = 0;
  566. }
  567. $return['sum'][$type] += $row['#'];
  568. $return['rows'][] = $row;
  569. }
  570. $return['sum']['TOTAL'] = array_sum($return['sum']);
  571. $return['numRows'] = count($return['rows']);
  572. $GLOBALS['dbi']->freeResult($result);
  573. return $return;
  574. }
  575. /**
  576. * Returns JSon for log data with type: general
  577. *
  578. * @param int $start Unix Time: Start time for query
  579. * @param int $end Unix Time: End time for query
  580. *
  581. * @return array
  582. */
  583. public static function getJsonForLogDataTypeGeneral($start, $end)
  584. {
  585. $limitTypes = '';
  586. if (isset($_POST['limitTypes']) && $_POST['limitTypes']) {
  587. $limitTypes
  588. = 'AND argument REGEXP \'^(INSERT|SELECT|UPDATE|DELETE)\' ';
  589. }
  590. $query = 'SELECT TIME(event_time) as event_time, user_host, thread_id, ';
  591. $query .= 'server_id, argument, count(argument) as \'#\' ';
  592. $query .= 'FROM `mysql`.`general_log` ';
  593. $query .= 'WHERE command_type=\'Query\' ';
  594. $query .= 'AND event_time > FROM_UNIXTIME(' . $start . ') ';
  595. $query .= 'AND event_time < FROM_UNIXTIME(' . $end . ') ';
  596. $query .= $limitTypes . 'GROUP by argument'; // HAVING count > 1';
  597. $result = $GLOBALS['dbi']->tryQuery($query);
  598. $return = array('rows' => array(), 'sum' => array());
  599. $insertTables = array();
  600. $insertTablesFirst = -1;
  601. $i = 0;
  602. $removeVars = isset($_POST['removeVariables'])
  603. && $_POST['removeVariables'];
  604. while ($row = $GLOBALS['dbi']->fetchAssoc($result)) {
  605. preg_match('/^(\w+)\s/', $row['argument'], $match);
  606. $type = mb_strtolower($match[1]);
  607. if (! isset($return['sum'][$type])) {
  608. $return['sum'][$type] = 0;
  609. }
  610. $return['sum'][$type] += $row['#'];
  611. switch($type) {
  612. /** @noinspection PhpMissingBreakStatementInspection */
  613. case 'insert':
  614. // Group inserts if selected
  615. if ($removeVars
  616. && preg_match(
  617. '/^INSERT INTO (`|\'|"|)([^\s\\1]+)\\1/i',
  618. $row['argument'], $matches
  619. )
  620. ) {
  621. $insertTables[$matches[2]]++;
  622. if ($insertTables[$matches[2]] > 1) {
  623. $return['rows'][$insertTablesFirst]['#']
  624. = $insertTables[$matches[2]];
  625. // Add a ... to the end of this query to indicate that
  626. // there's been other queries
  627. $temp = $return['rows'][$insertTablesFirst]['argument'];
  628. $return['rows'][$insertTablesFirst]['argument']
  629. .= self::getSuspensionPoints(
  630. $temp[strlen($temp) - 1]
  631. );
  632. // Group this value, thus do not add to the result list
  633. continue 2;
  634. } else {
  635. $insertTablesFirst = $i;
  636. $insertTables[$matches[2]] += $row['#'] - 1;
  637. }
  638. }
  639. // No break here
  640. case 'update':
  641. // Cut off big inserts and updates,
  642. // but append byte count therefor
  643. if (mb_strlen($row['argument']) > 220) {
  644. $row['argument'] = mb_substr($row['argument'], 0, 200)
  645. . '... ['
  646. . implode(
  647. ' ',
  648. Util::formatByteDown(
  649. mb_strlen($row['argument']),
  650. 2,
  651. 2
  652. )
  653. )
  654. . ']';
  655. }
  656. break;
  657. default:
  658. break;
  659. }
  660. $return['rows'][] = $row;
  661. $i++;
  662. }
  663. $return['sum']['TOTAL'] = array_sum($return['sum']);
  664. $return['numRows'] = count($return['rows']);
  665. $GLOBALS['dbi']->freeResult($result);
  666. return $return;
  667. }
  668. /**
  669. * Return suspension points if needed
  670. *
  671. * @param string $lastChar Last char
  672. *
  673. * @return null|string Return suspension points if needed
  674. */
  675. public static function getSuspensionPoints($lastChar)
  676. {
  677. if ($lastChar != '.') {
  678. return '<br/>...';
  679. }
  680. return null;
  681. }
  682. /**
  683. * Returns JSon for logging vars
  684. *
  685. * @return array
  686. */
  687. public static function getJsonForLoggingVars()
  688. {
  689. if (isset($_POST['varName']) && isset($_POST['varValue'])) {
  690. $value = $GLOBALS['dbi']->escapeString($_POST['varValue']);
  691. if (! is_numeric($value)) {
  692. $value="'" . $value . "'";
  693. }
  694. if (! preg_match("/[^a-zA-Z0-9_]+/", $_POST['varName'])) {
  695. $GLOBALS['dbi']->query(
  696. 'SET GLOBAL ' . $_POST['varName'] . ' = ' . $value
  697. );
  698. }
  699. }
  700. $loggingVars = $GLOBALS['dbi']->fetchResult(
  701. 'SHOW GLOBAL VARIABLES WHERE Variable_name IN'
  702. . ' ("general_log","slow_query_log","long_query_time","log_output")',
  703. 0,
  704. 1
  705. );
  706. return $loggingVars;
  707. }
  708. /**
  709. * Returns JSon for query_analyzer
  710. *
  711. * @return array
  712. */
  713. public static function getJsonForQueryAnalyzer()
  714. {
  715. $return = array();
  716. if (strlen($_POST['database']) > 0) {
  717. $GLOBALS['dbi']->selectDb($_POST['database']);
  718. }
  719. if ($profiling = Util::profilingSupported()) {
  720. $GLOBALS['dbi']->query('SET PROFILING=1;');
  721. }
  722. // Do not cache query
  723. $query = preg_replace(
  724. '/^(\s*SELECT)/i',
  725. '\\1 SQL_NO_CACHE',
  726. $_POST['query']
  727. );
  728. $GLOBALS['dbi']->tryQuery($query);
  729. $return['affectedRows'] = $GLOBALS['cached_affected_rows'];
  730. $result = $GLOBALS['dbi']->tryQuery('EXPLAIN ' . $query);
  731. while ($row = $GLOBALS['dbi']->fetchAssoc($result)) {
  732. $return['explain'][] = $row;
  733. }
  734. // In case an error happened
  735. $return['error'] = $GLOBALS['dbi']->getError();
  736. $GLOBALS['dbi']->freeResult($result);
  737. if ($profiling) {
  738. $return['profiling'] = array();
  739. $result = $GLOBALS['dbi']->tryQuery(
  740. 'SELECT seq,state,duration FROM INFORMATION_SCHEMA.PROFILING'
  741. . ' WHERE QUERY_ID=1 ORDER BY seq'
  742. );
  743. while ($row = $GLOBALS['dbi']->fetchAssoc($result)) {
  744. $return['profiling'][]= $row;
  745. }
  746. $GLOBALS['dbi']->freeResult($result);
  747. }
  748. return $return;
  749. }
  750. }