* @link http://www.yiiframework.com/ * @copyright Copyright © 2008-2011 Yii Software LLC * @license http://www.yiiframework.com/license/ */ /** * CProfileLogRoute displays the profiling results in Web page. * * The profiling is done by calling {@link YiiBase::beginProfile()} and {@link YiiBase::endProfile()}, * which marks the begin and end of a code block. * * CProfileLogRoute supports two types of report by setting the {@link setReport report} property: * * * @author Qiang Xue * @version $Id: CProfileLogRoute.php 3204 2011-05-05 21:36:32Z alexander.makarow $ * @package system.logging * @since 2.0 */ class CProfileLogRoute extends CWebLogRoute { /** * @var boolean whether to aggregate results according to profiling tokens. * If false, the results will be aggregated by categories. * Defaults to true. Note that this property only affects the summary report * that is enabled when {@link report} is 'summary'. */ public $groupByToken = true; /** * @var string type of profiling report to display */ private $_report = 'summary'; /** * Initializes the route. * This method is invoked after the route is created by the route manager. */ public function init() { $this->levels = CLogger::LEVEL_PROFILE; } /** * @return string the type of the profiling report to display. Defaults to 'summary'. */ public function getReport() { return $this->_report; } /** * @param string $value the type of the profiling report to display. Valid values include 'summary' and 'callstack'. */ public function setReport($value) { if ($value === 'summary' || $value === 'callstack') $this->_report = $value; else throw new CException(Yii::t('yii', 'CProfileLogRoute.report "{report}" is invalid. Valid values include "summary" and "callstack".', array('{report}' => $value))); } /** * Displays the log messages. * @param array $logs list of log messages */ public function processLogs($logs) { $app = Yii::app(); if (!($app instanceof CWebApplication) || $app->getRequest()->getIsAjaxRequest()) return; if ($this->getReport() === 'summary') $this->displaySummary($logs); else $this->displayCallstack($logs); } /** * Displays the callstack of the profiling procedures for display. * @param array $logs list of logs */ protected function displayCallstack($logs) { $stack = array(); $results = array(); $n = 0; foreach ($logs as $log) { if ($log[1] !== CLogger::LEVEL_PROFILE) continue; $message = $log[0]; if (!strncasecmp($message, 'begin:', 6)) { $log[0] = substr($message, 6); $log[4] = $n; $stack[] = $log; $n++; } elseif (!strncasecmp($message, 'end:', 4)) { $token = substr($message, 4); if (($last = array_pop($stack)) !== null && $last[0] === $token) { $delta = $log[3] - $last[3]; $results[$last[4]] = array($token, $delta, count($stack)); } else throw new CException(Yii::t('yii', 'CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.', array('{token}' => $token))); } } // remaining entries should be closed here $now = microtime(true); while (($last = array_pop($stack)) !== null) $results[$last[4]] = array($last[0], $now - $last[3], count($stack)); ksort($results); $this->render('profile-callstack', $results); } /** * Displays the summary report of the profiling result. * @param array $logs list of logs */ protected function displaySummary($logs) { $stack = array(); foreach ($logs as $log) { if ($log[1] !== CLogger::LEVEL_PROFILE) continue; $message = $log[0]; if (!strncasecmp($message, 'begin:', 6)) { $log[0] = substr($message, 6); $stack[] = $log; } elseif (!strncasecmp($message, 'end:', 4)) { $token = substr($message, 4); if (($last = array_pop($stack)) !== null && $last[0] === $token) { $delta = $log[3] - $last[3]; if (!$this->groupByToken) $token = $log[2]; if (isset($results[$token])) $results[$token] = $this->aggregateResult($results[$token], $delta); else $results[$token] = array($token, 1, $delta, $delta, $delta); } else throw new CException(Yii::t('yii', 'CProfileLogRoute found a mismatching code block "{token}". Make sure the calls to Yii::beginProfile() and Yii::endProfile() be properly nested.', array('{token}' => $token))); } } $now = microtime(true); while (($last = array_pop($stack)) !== null) { $delta = $now - $last[3]; $token = $this->groupByToken ? $last[0] : $last[2]; if (isset($results[$token])) $results[$token] = $this->aggregateResult($results[$token], $delta); else $results[$token] = array($token, 1, $delta, $delta, $delta); } $entries = array_values($results); $func = create_function('$a,$b', 'return $a[4]<$b[4]?1:0;'); usort($entries, $func); $this->render('profile-summary', $entries); } /** * Aggregates the report result. * @param array $result log result for this code block * @param float $delta time spent for this code block * @return array */ protected function aggregateResult($result, $delta) { list($token, $calls, $min, $max, $total) = $result; if ($delta < $min) $min = $delta; elseif ($delta > $max) $max = $delta; $calls++; $total += $delta; return array($token, $calls, $min, $max, $total); } }