Commit 5095401a by Alexander Makarov

Merge pull request #1698 from Ragazzo/debug_module_profile_panel_improvements

improved debug module profile panel
parents 9e66e08b 9a416f3c
...@@ -53,7 +53,7 @@ class Db extends Base ...@@ -53,7 +53,7 @@ class Db extends Base
'pageSize' => 10, 'pageSize' => 10,
], ],
'sort' => [ 'sort' => [
'attributes' => ['duration','type','query'], 'attributes' => ['duration', 'type', 'query'],
'defaultOrder' => [ 'defaultOrder' => [
'duration' => SORT_DESC, 'duration' => SORT_DESC,
], ],
......
...@@ -22,7 +22,7 @@ class Log extends Base ...@@ -22,7 +22,7 @@ class Log extends Base
public $category; public $category;
/** /**
* @var integer ajax attribute input search value * @var integer message attribute input search value
*/ */
public $message; public $message;
...@@ -59,7 +59,7 @@ class Log extends Base ...@@ -59,7 +59,7 @@ class Log extends Base
'pageSize' => 10, 'pageSize' => 10,
], ],
'sort' => [ 'sort' => [
'attributes' => ['time','level','category','message'], 'attributes' => ['time', 'level', 'category', 'message'],
], ],
]); ]);
......
<?php
namespace yii\debug\models\search;
use yii\data\ArrayDataProvider;
use yii\debug\components\search\Filter;
/**
* Profile represents the model behind the search form about current request profiling log.
*/
class Profile extends Base
{
/**
* @var string method attribute input search value
*/
public $category;
/**
* @var integer info attribute input search value
*/
public $info;
public function rules()
{
return [
[['category', 'info'], 'safe'],
];
}
/**
* @inheritdoc
*/
public function attributeLabels()
{
return [
'category' => 'Category',
'info' => 'Info',
];
}
/**
* Returns data provider with filled models. Filter applied if needed.
* @param array $params
* @param array $models
* @return \yii\data\ArrayDataProvider
*/
public function search($params, $models)
{
$dataProvider = new ArrayDataProvider([
'allModels' => $models,
'pagination' => [
'pageSize' => 10,
],
'sort' => [
'attributes' => ['category', 'info', 'duration'],
'defaultOrder' => [
'duration' => SORT_DESC,
],
],
]);
if (!($this->load($params) && $this->validate())) {
return $dataProvider;
}
$filter = new Filter();
$this->addCondition($filter, 'category', true);
$this->addCondition($filter, 'info', true);
$dataProvider->allModels = $filter->filter($models);
return $dataProvider;
}
}
...@@ -53,9 +53,9 @@ class DbPanel extends Panel ...@@ -53,9 +53,9 @@ class DbPanel extends Panel
public function getDetail() public function getDetail()
{ {
$searchModel = new Db(); $searchModel = new Db();
$dataProvider = $searchModel->search($_GET, $this->getModels()); $dataProvider = $searchModel->search(Yii::$app->request->get(), $this->getModels());
return Yii::$app->view->render('panels/db/detail',[ return Yii::$app->view->render('panels/db/detail', [
'panel' => $this, 'panel' => $this,
'dataProvider' => $dataProvider, 'dataProvider' => $dataProvider,
'searchModel' => $searchModel, 'searchModel' => $searchModel,
...@@ -121,7 +121,7 @@ class DbPanel extends Panel ...@@ -121,7 +121,7 @@ class DbPanel extends Panel
*/ */
protected function getModels() protected function getModels()
{ {
if ($this->_models === null || $refresh) { if ($this->_models === null) {
$this->_models = []; $this->_models = [];
$timings = $this->calculateTimings(); $timings = $this->calculateTimings();
......
...@@ -33,13 +33,13 @@ class LogPanel extends Panel ...@@ -33,13 +33,13 @@ class LogPanel extends Panel
public function getSummary() public function getSummary()
{ {
return Yii::$app->view->render('panels/log/summary',['data' => $this->data, 'panel' => $this]); return Yii::$app->view->render('panels/log/summary', ['data' => $this->data, 'panel' => $this]);
} }
public function getDetail() public function getDetail()
{ {
$searchModel = new Log(); $searchModel = new Log();
$dataProvider = $searchModel->search($_GET, $this->getModels()); $dataProvider = $searchModel->search(Yii::$app->request->get(), $this->getModels());
return Yii::$app->view->render('panels/log/detail',[ return Yii::$app->view->render('panels/log/detail',[
'dataProvider' => $dataProvider, 'dataProvider' => $dataProvider,
......
...@@ -9,8 +9,8 @@ namespace yii\debug\panels; ...@@ -9,8 +9,8 @@ namespace yii\debug\panels;
use Yii; use Yii;
use yii\debug\Panel; use yii\debug\Panel;
use yii\helpers\Html;
use yii\log\Logger; use yii\log\Logger;
use yii\debug\models\search\Profile;
/** /**
* Debugger panel that collects and displays performance profiling info. * Debugger panel that collects and displays performance profiling info.
...@@ -20,6 +20,17 @@ use yii\log\Logger; ...@@ -20,6 +20,17 @@ use yii\log\Logger;
*/ */
class ProfilingPanel extends Panel class ProfilingPanel extends Panel
{ {
/**
* @var array profile messages timings
*/
private $_timings;
/**
* @var array current request profile timings
*/
private $_models;
public function getName() public function getName()
{ {
return 'Profiling'; return 'Profiling';
...@@ -27,25 +38,41 @@ class ProfilingPanel extends Panel ...@@ -27,25 +38,41 @@ class ProfilingPanel extends Panel
public function getSummary() public function getSummary()
{ {
$memory = sprintf('%.1f MB', $this->data['memory'] / 1048576); return Yii::$app->view->render('panels/profile/summary', [
$time = number_format($this->data['time'] * 1000) . ' ms'; 'memory' => sprintf('%.1f MB', $this->data['memory'] / 1048576),
$url = $this->getUrl(); 'time' => number_format($this->data['time'] * 1000) . ' ms',
'panel' => $this
return <<<EOD ]);
<div class="yii-debug-toolbar-block">
<a href="$url" title="Total request processing time was $time">Time <span class="label">$time</span></a>
</div>
<div class="yii-debug-toolbar-block">
<a href="$url" title="Peak memory consumption">Memory <span class="label">$memory</span></a>
</div>
EOD;
} }
public function getDetail() public function getDetail()
{ {
$searchModel = new Profile();
$dataProvider = $searchModel->search(Yii::$app->request->get(), $this->getModels());
return Yii::$app->view->render('panels/profile/detail', [
'panel' => $this,
'dataProvider' => $dataProvider,
'searchModel' => $searchModel,
'memory' => sprintf('%.1f MB', $this->data['memory'] / 1048576),
'time' => number_format($this->data['time'] * 1000) . ' ms',
]);
}
/**
* Calculates given request profile messages timings.
* @return array timings
*/
protected function calculateTimings()
{
if ($this->_timings !== null) {
return $this->_timings;
}
$messages = $this->data['messages']; $messages = $this->data['messages'];
$timings = []; $timings = [];
$stack = []; $stack = [];
foreach ($messages as $i => $log) { foreach ($messages as $i => $log) {
list($token, $level, $category, $timestamp, $traces) = $log; list($token, $level, $category, $timestamp, $traces) = $log;
if ($level == Logger::LEVEL_PROFILE_BEGIN) { if ($level == Logger::LEVEL_PROFILE_BEGIN) {
...@@ -62,36 +89,7 @@ EOD; ...@@ -62,36 +89,7 @@ EOD;
$timings[] = [count($stack), $last[0], $last[2], $now - $last[3], $last[4]]; $timings[] = [count($stack), $last[0], $last[2], $now - $last[3], $last[4]];
} }
$rows = []; return $this->_timings = $timings;
foreach ($timings as $timing) {
$time = sprintf('%.1f ms', $timing[3] * 1000);
$procedure = str_repeat('<span class="indent">→</span>', $timing[0]) . Html::encode($timing[1]);
$category = Html::encode($timing[2]);
$rows[] = "<tr><td style=\"width: 80px;\">$time</td><td style=\"width: 220px;\">$category</td><td>$procedure</td>";
}
$rows = implode("\n", $rows);
$memory = sprintf('%.1f MB', $this->data['memory'] / 1048576);
$time = number_format($this->data['time'] * 1000) . ' ms';
return <<<EOD
<h2>Performance Profiling</h2>
<p>Total processing time: <b>$time</b>; Peak memory: <b>$memory</b>.</p>
<table class="table table-condensed table-bordered table-striped table-hover" style="table-layout: fixed;">
<thead>
<tr>
<th style="width: 80px;">Time</th>
<th style="width: 220px;">Category</th>
<th>Procedure</th>
</tr>
</thead>
<tbody>
$rows
</tbody>
</table>
EOD;
} }
public function save() public function save()
...@@ -104,4 +102,27 @@ EOD; ...@@ -104,4 +102,27 @@ EOD;
'messages' => $messages, 'messages' => $messages,
]; ];
} }
/**
* Returns array of profiling models that can be used in data provider.
* @return array models
*/
protected function getModels()
{
if ($this->_models === null) {
$this->_models = [];
$timings = $this->calculateTimings();
foreach($timings as $profileTiming) {
$this->_models[] = [
'duration' => $profileTiming[3] * 1000, #in milliseconds
'category' => $profileTiming[2],
'info' => $profileTiming[1],
'level' => $profileTiming[0],
];
}
}
return $this->_models;
}
} }
...@@ -15,29 +15,26 @@ echo GridView::widget([ ...@@ -15,29 +15,26 @@ echo GridView::widget([
['class' => 'yii\grid\SerialColumn'], ['class' => 'yii\grid\SerialColumn'],
[ [
'attribute' => 'duration', 'attribute' => 'duration',
'value' => function ($data) 'value' => function ($data) {
{
return sprintf('%.1f ms',$data['duration']); return sprintf('%.1f ms',$data['duration']);
}, },
], ],
[ [
'attribute' => 'type', 'attribute' => 'type',
'value' => function ($data) 'value' => function ($data) {
{
return Html::encode(mb_strtoupper($data['type'],'utf8')); return Html::encode(mb_strtoupper($data['type'],'utf8'));
}, },
], ],
[ [
'attribute' => 'query', 'attribute' => 'query',
'value' => function ($data) 'value' => function ($data) {
{
$query = Html::encode($data['query']); $query = Html::encode($data['query']);
if (!empty($data['trace'])) { if (!empty($data['trace'])) {
$query .= Html::ul($data['trace'], [ $query .= Html::ul($data['trace'], [
'class' => 'trace', 'class' => 'trace',
'item' => function ($trace) { 'item' => function ($trace) {
return "<li>{$trace['file']}({$trace['line']})</li>"; return "<li>{$trace['file']} ({$trace['line']})</li>";
}, },
]); ]);
} }
......
<?php if ($queryCount): ?> <?php if ($queryCount): ?>
<div class="yii-debug-toolbar-block"> <div class="yii-debug-toolbar-block">
<a href="$url" title="Executed <?php echo $queryCount; ?> database queries which took <?php echo $queryTime; ?>."> <a href="<?php echo $panel->getUrl();?>" title="Executed <?php echo $queryCount; ?> database queries which took <?php echo $queryTime; ?>.">
DB <span class="label"><?php echo $queryCount; ?></span> <span class="label"><?php echo $queryTime; ?></span> DB <span class="label"><?php echo $queryCount; ?></span> <span class="label"><?php echo $queryTime; ?></span>
</a> </a>
</div> </div>
......
...@@ -12,7 +12,7 @@ echo GridView::widget([ ...@@ -12,7 +12,7 @@ echo GridView::widget([
'id' => 'log-panel-detailed-grid', 'id' => 'log-panel-detailed-grid',
'filterModel' => $searchModel, 'filterModel' => $searchModel,
'filterUrl' => $panel->getUrl(), 'filterUrl' => $panel->getUrl(),
'rowOptions' => function ($model, $key, $index, $grid){ 'rowOptions' => function ($model, $key, $index, $grid) {
switch($model['level']) { switch($model['level']) {
case Logger::LEVEL_ERROR : return ['class' => 'danger']; case Logger::LEVEL_ERROR : return ['class' => 'danger'];
case Logger::LEVEL_WARNING : return ['class' => 'warning']; case Logger::LEVEL_WARNING : return ['class' => 'warning'];
...@@ -24,8 +24,7 @@ echo GridView::widget([ ...@@ -24,8 +24,7 @@ echo GridView::widget([
['class' => 'yii\grid\SerialColumn'], ['class' => 'yii\grid\SerialColumn'],
[ [
'attribute' => 'time', 'attribute' => 'time',
'value' => function ($data) 'value' => function ($data) {
{
$timeInSeconds = $data['time'] / 1000; $timeInSeconds = $data['time'] / 1000;
$millisecondsDiff = (int)(($timeInSeconds - (int)$timeInSeconds) * 1000); $millisecondsDiff = (int)(($timeInSeconds - (int)$timeInSeconds) * 1000);
return date('H:i:s.',$timeInSeconds) . sprintf('%03d',$millisecondsDiff); return date('H:i:s.',$timeInSeconds) . sprintf('%03d',$millisecondsDiff);
...@@ -33,8 +32,7 @@ echo GridView::widget([ ...@@ -33,8 +32,7 @@ echo GridView::widget([
], ],
[ [
'attribute' => 'level', 'attribute' => 'level',
'value' => function ($data) 'value' => function ($data) {
{
return Logger::getLevelName($data['level']); return Logger::getLevelName($data['level']);
}, },
'filter' => [ 'filter' => [
...@@ -47,15 +45,13 @@ echo GridView::widget([ ...@@ -47,15 +45,13 @@ echo GridView::widget([
'category', 'category',
[ [
'attribute' => 'message', 'attribute' => 'message',
'value' => function ($data) 'value' => function ($data) {
{
$message = nl2br(Html::encode($data['message'])); $message = nl2br(Html::encode($data['message']));
if (!empty($data['trace'])) { if (!empty($data['trace'])) {
$message .= Html::ul($data['trace'], [ $message .= Html::ul($data['trace'], [
'class' => 'trace', 'class' => 'trace',
'item' => function ($trace) 'item' => function ($trace) {
{
return "<li>{$trace['file']} ({$trace['line']})</li>"; return "<li>{$trace['file']} ({$trace['line']})</li>";
} }
]); ]);
......
<?php
use yii\grid\GridView;
?>
<h1>Performance Profiling</h1>
<p>Total processing time: <b><?php echo $time; ?></b>; Peak memory: <b><?php echo $memory; ?></b>.</p>
<?php
echo GridView::widget([
'dataProvider' => $dataProvider,
'id' => 'profile-panel-detailed-grid',
'filterModel' => $searchModel,
'filterUrl' => $panel->getUrl(),
'columns' => [
['class' => 'yii\grid\SerialColumn'],
[
'attribute' => 'duration',
'value' => function ($data) {
return sprintf('%.1f ms',$data['duration']);
},
],
'category',
[
'attribute' => 'info',
'value' => function ($data) {
return str_repeat('<span class="indent">→</span>', $data['level']) . $data['info'];
},
'options' => [
'width' => '60%',
],
],
],
]);
?>
\ No newline at end of file
<div class="yii-debug-toolbar-block">
<a href="<?php echo $panel->getUrl(); ?>" title="Total request processing time was <?php echo $time; ?>">Time <span class="label"><?php echo $time; ?></span></a>
</div>
<div class="yii-debug-toolbar-block">
<a href="<?php echo $panel->getUrl(); ?>" title="Peak memory consumption">Memory <span class="label"><?php echo $memory; ?></span></a>
</div>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment