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.

174 line
4.8KB

  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;
  8. use Yii;
  9. use yii\base\InvalidConfigException;
  10. use yii\log\Target;
  11. /**
  12. * The debug LogTarget is used to store logs for later use in the debugger tool
  13. *
  14. * @author Qiang Xue <qiang.xue@gmail.com>
  15. * @since 2.0
  16. */
  17. class LogTarget extends Target
  18. {
  19. /**
  20. * @var Module
  21. */
  22. public $module;
  23. public $tag;
  24. /**
  25. * @param \yii\debug\Module $module
  26. * @param array $config
  27. */
  28. public function __construct($module, $config = [])
  29. {
  30. parent::__construct($config);
  31. $this->module = $module;
  32. $this->tag = uniqid();
  33. }
  34. /**
  35. * Exports log messages to a specific destination.
  36. * Child classes must implement this method.
  37. */
  38. public function export()
  39. {
  40. $path = $this->module->dataPath;
  41. if (!is_dir($path)) {
  42. mkdir($path);
  43. }
  44. $summary = $this->collectSummary();
  45. $dataFile = "$path/{$this->tag}.data";
  46. $data = [];
  47. foreach ($this->module->panels as $id => $panel) {
  48. $data[$id] = $panel->save();
  49. }
  50. $data['summary'] = $summary;
  51. file_put_contents($dataFile, serialize($data));
  52. $indexFile = "$path/index.data";
  53. $this->updateIndexFile($indexFile, $summary);
  54. }
  55. /**
  56. * Updates index file with summary log data
  57. *
  58. * @param string $indexFile path to index file
  59. * @param array $summary summary log data
  60. * @throws \yii\base\InvalidConfigException
  61. */
  62. private function updateIndexFile($indexFile, $summary)
  63. {
  64. touch($indexFile);
  65. if (($fp = @fopen($indexFile, 'r+')) === false) {
  66. throw new InvalidConfigException("Unable to open debug data index file: $indexFile");
  67. }
  68. @flock($fp, LOCK_EX);
  69. $manifest = '';
  70. while (($buffer = fgets($fp)) !== false) {
  71. $manifest .= $buffer;
  72. }
  73. if (!feof($fp) || empty($manifest)) {
  74. // error while reading index data, ignore and create new
  75. $manifest = [];
  76. } else {
  77. $manifest = unserialize($manifest);
  78. }
  79. $manifest[$this->tag] = $summary;
  80. $this->gc($manifest);
  81. ftruncate($fp, 0);
  82. rewind($fp);
  83. fwrite($fp, serialize($manifest));
  84. @flock($fp, LOCK_UN);
  85. @fclose($fp);
  86. }
  87. /**
  88. * Processes the given log messages.
  89. * This method will filter the given messages with [[levels]] and [[categories]].
  90. * And if requested, it will also export the filtering result to specific medium (e.g. email).
  91. * @param array $messages log messages to be processed. See [[\yii\log\Logger::messages]] for the structure
  92. * of each message.
  93. * @param boolean $final whether this method is called at the end of the current application
  94. */
  95. public function collect($messages, $final)
  96. {
  97. $this->messages = array_merge($this->messages, $messages);
  98. if ($final) {
  99. $this->export($this->messages);
  100. }
  101. }
  102. protected function gc(&$manifest)
  103. {
  104. if (count($manifest) > $this->module->historySize + 10) {
  105. $n = count($manifest) - $this->module->historySize;
  106. foreach (array_keys($manifest) as $tag) {
  107. $file = $this->module->dataPath . "/$tag.data";
  108. @unlink($file);
  109. unset($manifest[$tag]);
  110. if (--$n <= 0) {
  111. break;
  112. }
  113. }
  114. }
  115. }
  116. /**
  117. * Collects summary data of current request.
  118. * @return array
  119. */
  120. protected function collectSummary()
  121. {
  122. $request = Yii::$app->getRequest();
  123. $response = Yii::$app->getResponse();
  124. $summary = [
  125. 'tag' => $this->tag,
  126. 'url' => $request->getAbsoluteUrl(),
  127. 'ajax' => (int) $request->getIsAjax(),
  128. 'method' => $request->getMethod(),
  129. 'ip' => $request->getUserIP(),
  130. 'time' => time(),
  131. 'statusCode' => $response->statusCode,
  132. 'sqlCount' => $this->getSqlTotalCount(),
  133. ];
  134. if (isset($this->module->panels['mail'])) {
  135. $summary['mailCount'] = count($this->module->panels['mail']->getMessages());
  136. }
  137. return $summary;
  138. }
  139. /**
  140. * Returns total sql count executed in current request. If database panel is not configured
  141. * returns 0.
  142. * @return integer
  143. */
  144. protected function getSqlTotalCount()
  145. {
  146. if (!isset($this->module->panels['db'])) {
  147. return 0;
  148. }
  149. $profileLogs = $this->module->panels['db']->getProfileLogs();
  150. # / 2 because messages are in couple (begin/end)
  151. return count($profileLogs) / 2;
  152. }
  153. }