From d0556a138de3cdcd46a41b79a41cbaa8d87afbad Mon Sep 17 00:00:00 2001 From: Qiang Xue Date: Thu, 4 Jul 2013 22:02:48 -0400 Subject: [PATCH] debug toolbar WIP --- framework/yii/debug/LogTarget.php | 32 ++++++----- framework/yii/debug/Module.php | 30 +++++++++- framework/yii/debug/Panel.php | 45 +++++++++++++++ .../debug/controllers/DefaultController.php | 37 ++++++++++-- framework/yii/debug/panels/ConfigPanel.php | 50 ++++++++++++++++ framework/yii/debug/panels/LogPanel.php | 45 +++++++++++++++ framework/yii/debug/panels/RequestPanel.php | 57 +++++++++++++++++++ framework/yii/debug/views/default/index.php | 17 +++++- framework/yii/debug/views/default/toolbar.php | 57 ++++++++----------- framework/yii/log/DbTarget.php | 6 +- framework/yii/log/EmailTarget.php | 6 +- framework/yii/log/FileTarget.php | 6 +- framework/yii/log/Logger.php | 2 +- framework/yii/log/Target.php | 9 ++- 14 files changed, 324 insertions(+), 75 deletions(-) create mode 100644 framework/yii/debug/Panel.php create mode 100644 framework/yii/debug/panels/ConfigPanel.php create mode 100644 framework/yii/debug/panels/LogPanel.php create mode 100644 framework/yii/debug/panels/RequestPanel.php diff --git a/framework/yii/debug/LogTarget.php b/framework/yii/debug/LogTarget.php index d7fd98ff41..b2081bea80 100644 --- a/framework/yii/debug/LogTarget.php +++ b/framework/yii/debug/LogTarget.php @@ -16,32 +16,34 @@ use yii\log\Target; */ class LogTarget extends Target { + /** + * @var Module + */ + public $module; public $maxLogFiles = 20; + public function __construct($module, $config = array()) + { + parent::__construct($config); + $this->module = $module; + } + /** * Exports log messages to a specific destination. * Child classes must implement this method. - * @param array $messages the messages to be exported. See [[Logger::messages]] for the structure - * of each message. */ - public function export($messages) + public function export() { $path = Yii::$app->getRuntimePath() . '/debug'; if (!is_dir($path)) { mkdir($path); } - $file = $path . '/' . Yii::$app->getLog()->getTag() . '.log'; - $data = array( - 'messages' => $messages, - '_SERVER' => $_SERVER, - '_GET' => $_GET, - '_POST' => $_POST, - '_COOKIE' => $_COOKIE, - '_FILES' => empty($_FILES) ? array() : $_FILES, - '_SESSION' => empty($_SESSION) ? array() : $_SESSION, - 'memory' => memory_get_peak_usage(), - 'time' => microtime(true) - YII_BEGIN_TIME, - ); + $tag = Yii::$app->getLog()->getTag(); + $file = "$path/$tag.log"; + $data = array(); + foreach ($this->module->panels as $panel) { + $data[$panel->id] = $panel->save(); + } file_put_contents($file, json_encode($data)); } diff --git a/framework/yii/debug/Module.php b/framework/yii/debug/Module.php index a1f8aa05e4..e3cba818a6 100644 --- a/framework/yii/debug/Module.php +++ b/framework/yii/debug/Module.php @@ -18,19 +18,28 @@ use yii\helpers\Html; class Module extends \yii\base\Module { public $controllerNamespace = 'yii\debug\controllers'; - public $panels; + /** + * @var array|Panel[] + */ + public $panels = array(); public function init() { parent::init(); - Yii::$app->log->targets['debug'] = new LogTarget; + + foreach (array_merge($this->corePanels(), $this->panels) as $id => $config) { + $config['id'] = $id; + $this->panels[$id] = Yii::createObject($config); + } + + Yii::$app->getLog()->targets['debug'] = new LogTarget($this); Yii::$app->getView()->on(View::EVENT_END_BODY, array($this, 'renderToolbar')); } public function beforeAction($action) { Yii::$app->getView()->off(View::EVENT_END_BODY, array($this, 'renderToolbar')); - unset(Yii::$app->log->targets['debug']); + unset(Yii::$app->getLog()->targets['debug']); return parent::beforeAction($action); } @@ -49,4 +58,19 @@ class Module extends \yii\base\Module 'style' => 'display: none', )); } + + protected function corePanels() + { + return array( + 'config' => array( + 'class' => 'yii\debug\panels\ConfigPanel', + ), + 'request' => array( + 'class' => 'yii\debug\panels\RequestPanel', + ), + 'log' => array( + 'class' => 'yii\debug\panels\LogPanel', + ), + ); + } } diff --git a/framework/yii/debug/Panel.php b/framework/yii/debug/Panel.php new file mode 100644 index 0000000000..4db7f622d2 --- /dev/null +++ b/framework/yii/debug/Panel.php @@ -0,0 +1,45 @@ + + * @since 2.0 + */ +class Panel extends Component +{ + public $id; + public $data; + + public function getName() + { + return ''; + } + + public function getSummary() + { + return ''; + } + + public function getDetail() + { + return ''; + } + + public function save() + { + return null; + } + + public function load($data) + { + $this->data = $data; + } +} diff --git a/framework/yii/debug/controllers/DefaultController.php b/framework/yii/debug/controllers/DefaultController.php index 56d583f690..a80201b8d1 100644 --- a/framework/yii/debug/controllers/DefaultController.php +++ b/framework/yii/debug/controllers/DefaultController.php @@ -9,6 +9,7 @@ namespace yii\debug\controllers; use Yii; use yii\web\Controller; +use yii\web\HttpException; /** * @author Qiang Xue @@ -16,22 +17,48 @@ use yii\web\Controller; */ class DefaultController extends Controller { + /** @var \yii\debug\Module */ + public $module; public $layout = 'main'; - public function actionIndex($tag) + public function actionIndex($tag, $panel = null) { - return $this->render('index'); + $this->loadData($tag); + if (isset($this->module->panels[$panel])) { + $activePanel = $this->module->panels[$panel]; + } else { + $activePanel = reset($this->module->panels); + } + return $this->render('index', array( + 'tag' => $tag, + 'panels' => $this->module->panels, + 'activePanel' => $activePanel, + )); } public function actionToolbar($tag) + { + $this->loadData($tag); + return $this->renderPartial('toolbar', array( + 'panels' => $this->module->panels, + )); + } + + protected function loadData($tag) { $file = Yii::$app->getRuntimePath() . "/debug/$tag.log"; if (preg_match('/^[\w\-]+$/', $tag) && is_file($file)) { $data = json_decode(file_get_contents($file), true); - $data['tag'] = $tag; - return $this->renderPartial('toolbar', $data); + foreach ($this->module->panels as $id => $panel) { + if (isset($data[$panel->id])) { + $panel->load($data[$panel->id]); + } else { + // remove the panel since it has not received any data + unset($this->module->panels[$id]); + } + } } else { - return "Unable to find debug data tagged with '$tag'."; + throw new HttpException(404, "Unable to find debug data tagged with '$tag'."); } } } diff --git a/framework/yii/debug/panels/ConfigPanel.php b/framework/yii/debug/panels/ConfigPanel.php new file mode 100644 index 0000000000..e513caf196 --- /dev/null +++ b/framework/yii/debug/panels/ConfigPanel.php @@ -0,0 +1,50 @@ + + * @since 2.0 + */ +class ConfigPanel extends Panel +{ + public function getName() + { + return 'Config'; + } + + public function getSummary() + { + $link = Html::a('more details', array('index', 'tag' => $this->data['tag'])); + return << + PHP: {$this->data['phpVersion']}, + Yii: {$this->data['phpVersion']}, + $link + +EOD; + } + + public function getDetail() + { + return '

Config

'; + } + + public function save() + { + return array( + 'tag' => Yii::$app->getLog()->getTag(), + 'phpVersion' => PHP_VERSION, + 'yiiVersion' => Yii::getVersion(), + ); + } +} diff --git a/framework/yii/debug/panels/LogPanel.php b/framework/yii/debug/panels/LogPanel.php new file mode 100644 index 0000000000..91f62a7e88 --- /dev/null +++ b/framework/yii/debug/panels/LogPanel.php @@ -0,0 +1,45 @@ + + * @since 2.0 + */ +class LogPanel extends Panel +{ + public function getName() + { + return 'Logs'; + } + + public function getSummary() + { + $count = count($this->data['messages']); + return << +Log messages: $count + +EOD; + } + + public function getDetail() + { + return '

Logs

'; + } + + public function save() + { + return array( + 'messages' => Yii::$app->getLog()->targets['debug']->messages, + ); + } +} diff --git a/framework/yii/debug/panels/RequestPanel.php b/framework/yii/debug/panels/RequestPanel.php new file mode 100644 index 0000000000..ba98005f9b --- /dev/null +++ b/framework/yii/debug/panels/RequestPanel.php @@ -0,0 +1,57 @@ + + * @since 2.0 + */ +class RequestPanel extends Panel +{ + public function getName() + { + return 'Request'; + } + + public function getSummary() + { + $memory = sprintf('%.2fMB', $this->data['memory'] / 1048576); + $time = sprintf('%.3fs', $this->data['time']); + + return << +Peak memory: $memory + + +
+ Time spent: $time +
+EOD; + } + + public function getDetail() + { + return '

Request

'; + } + + public function save() + { + return array( + 'memory' => memory_get_peak_usage(), + 'time' => microtime(true) - YII_BEGIN_TIME, + 'SERVER' => $_SERVER, + 'GET' => $_GET, + 'POST' => $_POST, + 'COOKIE' => $_COOKIE, + 'FILES' => empty($_FILES) ? array() : $_FILES, + 'SESSION' => empty($_SESSION) ? array() : $_SESSION, + ); + } +} diff --git a/framework/yii/debug/views/default/index.php b/framework/yii/debug/views/default/index.php index 57cf853ab4..5af7755654 100644 --- a/framework/yii/debug/views/default/index.php +++ b/framework/yii/debug/views/default/index.php @@ -1 +1,16 @@ -here we are + + +getName()), array('debug/default/index', 'tag' => $tag, 'panel' => $panel->id)); ?>
+ + +getDetail(); ?> diff --git a/framework/yii/debug/views/default/toolbar.php b/framework/yii/debug/views/default/toolbar.php index 27f02f8f81..a7958fe5fc 100644 --- a/framework/yii/debug/views/default/toolbar.php +++ b/framework/yii/debug/views/default/toolbar.php @@ -1,40 +1,31 @@ - + +
-
- $tag)); ?> -
- -
- Peak memory: -
- -
- Time spent: -
- -
-
- -
-
+ + getSummary(); ?> +
diff --git a/framework/yii/log/DbTarget.php b/framework/yii/log/DbTarget.php index 16b1eab299..a828a729f2 100644 --- a/framework/yii/log/DbTarget.php +++ b/framework/yii/log/DbTarget.php @@ -72,16 +72,14 @@ class DbTarget extends Target /** * Stores log messages to DB. - * @param array $messages the messages to be exported. See [[Logger::messages]] for the structure - * of each message. */ - public function export($messages) + public function export() { $tableName = $this->db->quoteTableName($this->logTable); $sql = "INSERT INTO $tableName ([[level]], [[category]], [[log_time]], [[message]]) VALUES (:level, :category, :log_time, :message)"; $command = $this->db->createCommand($sql); - foreach ($messages as $message) { + foreach ($this->messages as $message) { $command->bindValues(array( ':level' => $message[1], ':category' => $message[2], diff --git a/framework/yii/log/EmailTarget.php b/framework/yii/log/EmailTarget.php index df4f9e0368..8ae1a888c6 100644 --- a/framework/yii/log/EmailTarget.php +++ b/framework/yii/log/EmailTarget.php @@ -38,13 +38,11 @@ class EmailTarget extends Target /** * Sends log messages to specified email addresses. - * @param array $messages the messages to be exported. See [[Logger::messages]] for the structure - * of each message. */ - public function export($messages) + public function export() { $body = ''; - foreach ($messages as $message) { + foreach ($this->messages as $message) { $body .= $this->formatMessage($message); } $body = wordwrap($body, 70); diff --git a/framework/yii/log/FileTarget.php b/framework/yii/log/FileTarget.php index c4cd40dacb..74a33be890 100644 --- a/framework/yii/log/FileTarget.php +++ b/framework/yii/log/FileTarget.php @@ -65,14 +65,12 @@ class FileTarget extends Target /** * Sends log messages to specified email addresses. - * @param array $messages the messages to be exported. See [[Logger::messages]] for the structure - * of each message. * @throws InvalidConfigException if unable to open the log file for writing */ - public function export($messages) + public function export() { $text = ''; - foreach ($messages as $message) { + foreach ($this->messages as $message) { $text .= $this->formatMessage($message); } if (($fp = @fopen($this->logFile, 'a')) === false) { diff --git a/framework/yii/log/Logger.php b/framework/yii/log/Logger.php index e1d81e2220..e2b480745f 100644 --- a/framework/yii/log/Logger.php +++ b/framework/yii/log/Logger.php @@ -124,7 +124,7 @@ class Logger extends Component */ public $messages = array(); /** - * @var array the log targets. Each array element represents a single [[Target|log target]] instance + * @var array|Target[] the log targets. Each array element represents a single [[Target|log target]] instance * or the configuration for creating the log target instance. */ public $targets = array(); diff --git a/framework/yii/log/Target.php b/framework/yii/log/Target.php index a3276fc8f4..2f6f73f3ee 100644 --- a/framework/yii/log/Target.php +++ b/framework/yii/log/Target.php @@ -68,18 +68,17 @@ abstract class Target extends Component public $exportInterval = 1000; /** * @var array the messages that are retrieved from the logger so far by this log target. + * Please refer to [[Logger::messages]] for the details about the message structure. */ public $messages = array(); private $_levels = 0; /** - * Exports log messages to a specific destination. + * Exports log [[messages]] to a specific destination. * Child classes must implement this method. - * @param array $messages the messages to be exported. See [[Logger::messages]] for the structure - * of each message. */ - abstract public function export($messages); + abstract public function export(); /** * Processes the given log messages. @@ -97,7 +96,7 @@ abstract class Target extends Component if (($context = $this->getContextMessage()) !== '') { $this->messages[] = array($context, Logger::LEVEL_INFO, 'application', YII_BEGIN_TIME); } - $this->export($this->messages); + $this->export(); $this->messages = array(); } }