You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

212 lines
5.4KB

  1. <?php
  2. /**
  3. * @link http://www.yiiframework.com/
  4. * @copyright Copyright (c) 2008 Yii Software LLC
  5. * @license http://www.yiiframework.com/license/
  6. */
  7. namespace yii\debug\panels;
  8. use Yii;
  9. use yii\debug\Panel;
  10. use yii\log\Logger;
  11. use yii\debug\models\search\Db;
  12. /**
  13. * Debugger panel that collects and displays database queries performed.
  14. *
  15. * @property array $profileLogs This property is read-only.
  16. * @property string $summaryName Short name of the panel, which will be use in summary. This property is
  17. * read-only.
  18. *
  19. * @author Qiang Xue <qiang.xue@gmail.com>
  20. * @since 2.0
  21. */
  22. class DbPanel extends Panel
  23. {
  24. /**
  25. * @var integer the threshold for determining whether the request has involved
  26. * critical number of DB queries. If the number of queries exceeds this number,
  27. * the execution is considered taking critical number of DB queries.
  28. */
  29. public $criticalQueryThreshold;
  30. /**
  31. * @var array db queries info extracted to array as models, to use with data provider.
  32. */
  33. private $_models;
  34. /**
  35. * @var array current database request timings
  36. */
  37. private $_timings;
  38. /**
  39. * @inheritdoc
  40. */
  41. public function getName()
  42. {
  43. return 'Database';
  44. }
  45. /**
  46. * @return string short name of the panel, which will be use in summary.
  47. */
  48. public function getSummaryName()
  49. {
  50. return 'DB';
  51. }
  52. /**
  53. * @inheritdoc
  54. */
  55. public function getSummary()
  56. {
  57. $timings = $this->calculateTimings();
  58. $queryCount = count($timings);
  59. $queryTime = number_format($this->getTotalQueryTime($timings) * 1000) . ' ms';
  60. return Yii::$app->view->render('panels/db/summary', [
  61. 'timings' => $this->calculateTimings(),
  62. 'panel' => $this,
  63. 'queryCount' => $queryCount,
  64. 'queryTime' => $queryTime,
  65. ]);
  66. }
  67. /**
  68. * @inheritdoc
  69. */
  70. public function getDetail()
  71. {
  72. $searchModel = new Db();
  73. $dataProvider = $searchModel->search(Yii::$app->request->getQueryParams(), $this->getModels());
  74. return Yii::$app->view->render('panels/db/detail', [
  75. 'panel' => $this,
  76. 'dataProvider' => $dataProvider,
  77. 'searchModel' => $searchModel,
  78. ]);
  79. }
  80. /**
  81. * Calculates given request profile timings.
  82. *
  83. * @return array timings [token, category, timestamp, traces, nesting level, elapsed time]
  84. */
  85. protected function calculateTimings()
  86. {
  87. if ($this->_timings === null) {
  88. $this->_timings = Yii::getLogger()->calculateTimings($this->data['messages']);
  89. }
  90. return $this->_timings;
  91. }
  92. /**
  93. * @inheritdoc
  94. */
  95. public function save()
  96. {
  97. return ['messages' => $this->getProfileLogs()];
  98. }
  99. /**
  100. * Returns all profile logs of the current request for this panel. It includes categories such as:
  101. * 'yii\db\Command::query', 'yii\db\Command::execute'.
  102. * @return array
  103. */
  104. public function getProfileLogs()
  105. {
  106. $target = $this->module->logTarget;
  107. return $target->filterMessages($target->messages, Logger::LEVEL_PROFILE, ['yii\db\Command::query', 'yii\db\Command::execute']);
  108. }
  109. /**
  110. * Returns total query time.
  111. *
  112. * @param array $timings
  113. * @return integer total time
  114. */
  115. protected function getTotalQueryTime($timings)
  116. {
  117. $queryTime = 0;
  118. foreach ($timings as $timing) {
  119. $queryTime += $timing['duration'];
  120. }
  121. return $queryTime;
  122. }
  123. /**
  124. * Returns an array of models that represents logs of the current request.
  125. * Can be used with data providers such as \yii\data\ArrayDataProvider.
  126. * @return array models
  127. */
  128. protected function getModels()
  129. {
  130. if ($this->_models === null) {
  131. $this->_models = [];
  132. $timings = $this->calculateTimings();
  133. foreach ($timings as $seq => $dbTiming) {
  134. $this->_models[] = [
  135. 'type' => $this->getQueryType($dbTiming['info']),
  136. 'query' => $dbTiming['info'],
  137. 'duration' => ($dbTiming['duration'] * 1000), // in milliseconds
  138. 'trace' => $dbTiming['trace'],
  139. 'timestamp' => ($dbTiming['timestamp'] * 1000), // in milliseconds
  140. 'seq' => $seq,
  141. ];
  142. }
  143. }
  144. return $this->_models;
  145. }
  146. /**
  147. * Returns database query type.
  148. *
  149. * @param string $timing timing procedure string
  150. * @return string query type such as select, insert, delete, etc.
  151. */
  152. protected function getQueryType($timing)
  153. {
  154. $timing = ltrim($timing);
  155. preg_match('/^([a-zA-z]*)/', $timing, $matches);
  156. return count($matches) ? $matches[0] : '';
  157. }
  158. /**
  159. * Check if given queries count is critical according settings.
  160. *
  161. * @param integer $count queries count
  162. * @return boolean
  163. */
  164. public function isQueryCountCritical($count)
  165. {
  166. return (($this->criticalQueryThreshold !== null) && ($count > $this->criticalQueryThreshold));
  167. }
  168. /**
  169. * Returns array query types
  170. *
  171. * @return array
  172. * @since 2.0.3
  173. */
  174. public function getTypes()
  175. {
  176. return array_reduce(
  177. $this->_models,
  178. function ($result, $item) {
  179. $result[$item['type']] = $item['type'];
  180. return $result;
  181. },
  182. []
  183. );
  184. }
  185. }