replay_query_log.php 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. <?php
  2. // Copyright (C) 2010-2012 Combodo SARL
  3. //
  4. // This file is part of iTop.
  5. //
  6. // iTop is free software; you can redistribute it and/or modify
  7. // it under the terms of the GNU Affero General Public License as published by
  8. // the Free Software Foundation, either version 3 of the License, or
  9. // (at your option) any later version.
  10. //
  11. // iTop is distributed in the hope that it will be useful,
  12. // but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. // GNU Affero General Public License for more details.
  15. //
  16. // You should have received a copy of the GNU Affero General Public License
  17. // along with iTop. If not, see <http://www.gnu.org/licenses/>
  18. /**
  19. * Replay the query log made when log_queries = 1
  20. *
  21. * @copyright Copyright (C) 2010-2012 Combodo SARL
  22. * @license http://opensource.org/licenses/AGPL-3.0
  23. */
  24. function LogResult($sString)
  25. {
  26. file_put_contents(APPROOT.'data/queries.results.log', "\n".$sString, FILE_APPEND);
  27. }
  28. function LogBenchmarkCSV()
  29. {
  30. $aValues = array();
  31. foreach (func_get_args() as $arg)
  32. {
  33. if (is_string($arg))
  34. {
  35. $aValues[] = '"'.str_replace('"', '""', $arg).'"';
  36. }
  37. else
  38. {
  39. $aValues[] = (string) $arg;
  40. }
  41. }
  42. $sLine = implode(';', $aValues); // the preferred for MS Excel
  43. file_put_contents(APPROOT.'data/queries.benchmark.csv', "\n".$sLine, FILE_APPEND);
  44. }
  45. class QueryLogEntry
  46. {
  47. public function __construct($aLogEntryId, $aLogEntryData)
  48. {
  49. $this->aErrors = array();
  50. $this->sSql = '';
  51. $this->MakeDuration = 0;
  52. $this->fExecDuration = 0;
  53. $this->iTableCount = 0;
  54. $this->aRows = array();
  55. $this->sLogId = $aLogEntryId;
  56. $this->sOql = $aLogEntryData['oql'];
  57. $this->sOqlHtml = htmlentities($this->sOql, ENT_QUOTES, 'UTF-8');
  58. $aQueryData = unserialize($aLogEntryData['data']);
  59. $this->oFilter = $aQueryData['filter'];
  60. $this->sClass = $this->oFilter->GetClass();
  61. $this->aArgs = $aQueryData['args'];
  62. $iRepeat = utils::ReadParam('repeat', 3);
  63. if ($aQueryData['type'] == 'select')
  64. {
  65. $this->aOrderBy = $aQueryData['order_by'];
  66. $this->aAttToLoad = $aQueryData['att_to_load'];
  67. $this->aExtendedDataSpec = $aQueryData['extended_data_spec'];
  68. $this->iLimitCount = $aQueryData['limit_count'];
  69. $this->iLimitStart = $aQueryData['limit_start'];
  70. $this->bGetCount = $aQueryData['is_count'];
  71. if ($this->bGetCount)
  72. {
  73. $this->sQueryType = 'COUNT';
  74. $this->sQueryDesc = '';
  75. }
  76. else
  77. {
  78. $this->sQueryType = 'LIST';
  79. $this->sQueryDesc = "limit count: $this->iLimitCount";
  80. $this->sQueryDesc .= "; limit start: $this->iLimitStart";
  81. if (count($this->aOrderBy) > 0)
  82. {
  83. $this->sQueryDesc .= "; order by: ".implode(',', array_keys($this->aOrderBy));
  84. }
  85. if (is_array($this->aAttToLoad))
  86. {
  87. $this->sQueryDesc .= "; attributes: ".implode(',', array_keys($this->aAttToLoad));
  88. }
  89. }
  90. $fRefTime = MyHelpers::getmicrotime();
  91. try
  92. {
  93. for($i = 0 ; $i < $iRepeat ; $i++)
  94. {
  95. $this->sSql = MetaModel::MakeSelectQuery($this->oFilter, $this->aOrderBy, $this->aArgs, $this->aAttToLoad, $this->aExtendedDataSpec, $this->iLimitCount, $this->iLimitStart, $this->bGetCount);
  96. }
  97. }
  98. catch(Exception $e)
  99. {
  100. $this->aErrors[] = "Failed to create the SQL:".$e->getMessage();
  101. }
  102. $this->fMakeDuration = (MyHelpers::getmicrotime() - $fRefTime) / $iRepeat;
  103. }
  104. elseif ($aQueryData['type'] == 'group_by')
  105. {
  106. $this->aGroupByExpr = $aQueryData['group_by_expr'];
  107. $this->sQueryType = 'GROUP BY';
  108. $aGroupedBy = array();
  109. foreach ($this->aGroupByExpr as $oExpr)
  110. {
  111. $aGroupedBy[] = $oExpr->Render();
  112. }
  113. $this->sQueryDesc = implode(', ', $aGroupedBy);
  114. $fRefTime = MyHelpers::getmicrotime();
  115. try
  116. {
  117. for($i = 0 ; $i < $iRepeat ; $i++)
  118. {
  119. $this->sSql = MetaModel::MakeGroupByQuery($this->oFilter, $this->aArgs, $this->aGroupByExpr);
  120. }
  121. }
  122. catch(Exception $e)
  123. {
  124. $this->aErrors[] = "Failed to create the SQL:".$e->getMessage();
  125. }
  126. $this->fMakeDuration = (MyHelpers::getmicrotime() - $fRefTime) / $iRepeat;
  127. }
  128. else
  129. {
  130. // unsupported
  131. $this->sQueryType = 'ERROR';
  132. $this->sQueryDesc = "Unkown type of query: ".$aQueryData['type'];
  133. }
  134. }
  135. public function Exec()
  136. {
  137. if ($this->sSql != '')
  138. {
  139. $iRepeat = utils::ReadParam('repeat', 3);
  140. try
  141. {
  142. $fRefTime = MyHelpers::getmicrotime();
  143. for($i = 0 ; $i < $iRepeat ; $i++)
  144. {
  145. $resQuery = CMDBSource::Query($this->sSql);
  146. }
  147. $this->fExecDuration = (MyHelpers::getmicrotime() - $fRefTime) / $iRepeat;
  148. // This is not relevant...
  149. if (preg_match_all('|\s*JOIN\s*\(\s*`|', $this->sSql, $aMatches)) // JOIN (`mytable...
  150. {
  151. $this->iTableCount = 1 + count($aMatches[0]);
  152. }
  153. else
  154. {
  155. $this->iTableCount = 1;
  156. }
  157. }
  158. catch (Exception $e)
  159. {
  160. $this->aErrors[] = "Failed to execute the SQL:".$e->getMessage();
  161. $resQuery = null;
  162. }
  163. if ($resQuery)
  164. {
  165. while ($aRow = CMDBSource::FetchArray($resQuery))
  166. {
  167. $this->aRows[] = $aRow;
  168. }
  169. CMDBSource::FreeResult($resQuery);
  170. }
  171. }
  172. }
  173. public function HasErrors()
  174. {
  175. return (count($this->aErrors) > 0);
  176. }
  177. public function Display($oP)
  178. {
  179. $oP->p($this->sOqlHtml);
  180. $oP->p($this->sQueryType);
  181. $oP->p($this->sQueryDesc);
  182. foreach ($this->aErrors as $sError)
  183. {
  184. $oP->p($sError);
  185. }
  186. }
  187. }
  188. /////////////////////////////////////////////////////////////////////////////
  189. //
  190. // Main program
  191. //
  192. /////////////////////////////////////////////////////////////////////////////
  193. require_once('../approot.inc.php');
  194. require_once(APPROOT.'/application/application.inc.php');
  195. require_once(APPROOT.'/application/ajaxwebpage.class.inc.php');
  196. require_once(APPROOT.'/application/startup.inc.php');
  197. $operation = utils::ReadParam('operation', '');
  198. require_once(APPROOT.'/application/loginwebpage.class.inc.php');
  199. LoginWebPage::DoLogin(); // Check user rights and prompt if needed
  200. $oP = new WebPage('Replay queries.log');
  201. ini_set('memory_limit', '512M');
  202. require_once(APPROOT.'/data/queries.log');
  203. $iCount = count($aQueriesLog);
  204. $oP->p("Nombre de requêtes: ".$iCount);
  205. $sOperation = utils::ReadParam('operation', '');
  206. switch ($sOperation)
  207. {
  208. case '':
  209. default:
  210. $oP->add("<ol>\n");
  211. foreach ($aQueriesLog as $sQueryId => $aOqlData)
  212. {
  213. $sOql = $aOqlData['oql'];
  214. $sOqlHtml = htmlentities($sOql, ENT_QUOTES, 'UTF-8');
  215. $oP->add("<li>$sOqlHtml <a href=\"?operation=zoom&query=$sQueryId\">zoom</a></li>\n");
  216. }
  217. $oP->add("</ol>\n");
  218. $oP->add("<form action=\"?operation=benchmark&repeat=3\" method=\"post\">\n");
  219. $oP->add("<input type=\"submit\" value=\"Benchmark (3 repeats)!\">\n");
  220. $oP->add("</form>\n");
  221. $oP->add("<form action=\"?operation=check\" method=\"post\">\n");
  222. $oP->add("<input type=\"submit\" value=\"Check!\">\n");
  223. $oP->add("</form>\n");
  224. break;
  225. case 'zoom':
  226. $sQueryId = utils::ReadParam('query', '', false, 'raw_data');
  227. $oP->add("<h2>Zoom on query</h2>\n");
  228. $oQuery = new QueryLogEntry($sQueryId, $aQueriesLog[$sQueryId]);
  229. $oQuery->Exec();
  230. $oQuery->Display($oP);
  231. $oP->add("<pre>$oQuery->sSql</pre>\n");
  232. $oP->p("Tables: $oQuery->iTableCount");
  233. if (strlen($oQuery->sSql) > 0)
  234. {
  235. $aExplain = CMDBSource::ExplainQuery($oQuery->sSql);
  236. $oP->add("<h4>Explain</h4>\n");
  237. $oP->add("<table border=\"1\">\n");
  238. foreach ($aExplain as $aRow)
  239. {
  240. $oP->add(" <tr>\n");
  241. $oP->add(" <td>".implode('</td><td>', $aRow)."</td>\n");
  242. $oP->add(" </tr>\n");
  243. }
  244. $oP->add("</table>\n");
  245. }
  246. if (count($oQuery->aRows))
  247. {
  248. $oP->add("<h4>Values</h4>\n");
  249. $oP->add("<table border=\"1\">\n");
  250. foreach ($oQuery->aRows as $iRow => $aRow)
  251. {
  252. $oP->add(" <tr>\n");
  253. $oP->add(" <td>".implode('</td><td>', $aRow)."</td>\n");
  254. $oP->add(" </tr>\n");
  255. }
  256. $oP->add("</table>\n");
  257. }
  258. else
  259. {
  260. $oP->p("No data");
  261. }
  262. break;
  263. case 'check':
  264. $oP->add("<h2>List queries in error</h2>\n");
  265. foreach ($aQueriesLog as $sQueryId => $aOqlData)
  266. {
  267. $oQuery = new QueryLogEntry($sQueryId, $aOqlData);
  268. $oQuery->Exec();
  269. if ($oQuery->HasErrors())
  270. {
  271. $oQuery->Display($oP);
  272. $oP->p("<a href=\"?operation=zoom&query=$sQueryId\">zoom</a>");
  273. }
  274. }
  275. break;
  276. case 'benchmark':
  277. $oP->add("<h2>Create data/queries.xxx reports</h2>\n");
  278. // Reset the log contents
  279. file_put_contents(APPROOT.'data/queries.results.log', date('Y-m-d H:i:s')."\n");
  280. file_put_contents(APPROOT.'data/queries.benchmark.csv', '');
  281. LogBenchmarkCSV('type', 'properties', 'make duration', 'class', 'tables', 'query length', 'exec duration', 'rows', 'oql');
  282. $iErrors = 0;
  283. foreach ($aQueriesLog as $sQueryId => $aOqlData)
  284. {
  285. $oQuery = new QueryLogEntry($sQueryId, $aOqlData);
  286. $oQuery->Exec();
  287. LogResult('-----------------------------------------------------------');
  288. LogResult($oQuery->sOql);
  289. LogResult($oQuery->sQueryType);
  290. if (strlen($oQuery->sQueryDesc) > 0)
  291. {
  292. LogResult($oQuery->sQueryDesc);
  293. }
  294. if ($oQuery->HasErrors())
  295. {
  296. foreach($oQuery->aErrors as $sError)
  297. {
  298. LogResult($sError);
  299. $iErrors++;
  300. }
  301. }
  302. else
  303. {
  304. LogResult("row count = ".count($oQuery->aRows));
  305. foreach($oQuery->aRows as $iRow => $aRow)
  306. {
  307. LogResult("row: ".serialize($aRow));
  308. if ($iRow > 100) break;
  309. }
  310. LogBenchmarkCSV($oQuery->sQueryType, $oQuery->sQueryDesc, sprintf('%1.3f', round($oQuery->fMakeDuration, 3)), $oQuery->sClass, $oQuery->iTableCount, strlen($oQuery->sSql), sprintf('%1.4f', round($oQuery->fExecDuration, 4)), count($oQuery->aRows), $oQuery->sOql);
  311. }
  312. }
  313. }
  314. $oP->output();
  315. ?>