summaryrefslogtreecommitdiff
path: root/framework/logging/CProfileLogRoute.php
diff options
context:
space:
mode:
Diffstat (limited to 'framework/logging/CProfileLogRoute.php')
-rw-r--r--framework/logging/CProfileLogRoute.php202
1 files changed, 202 insertions, 0 deletions
diff --git a/framework/logging/CProfileLogRoute.php b/framework/logging/CProfileLogRoute.php
new file mode 100644
index 0000000..f1d5a04
--- /dev/null
+++ b/framework/logging/CProfileLogRoute.php
@@ -0,0 +1,202 @@
+<?php
+/**
+ * CProfileLogRoute class file.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @link http://www.yiiframework.com/
+ * @copyright Copyright &copy; 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:
+ * <ul>
+ * <li>summary: list the execution time of every marked code block</li>
+ * <li>callstack: list the mark code blocks in a hierarchical view reflecting their calling sequence.</li>
+ * </ul>
+ *
+ * @property string $report The type of the profiling report to display. Defaults to 'summary'.
+ *
+ * @author Qiang Xue <qiang.xue@gmail.com>
+ * @version $Id: CProfileLogRoute.php 3515 2011-12-28 12:29:24Z mdomba $
+ * @package system.logging
+ * @since 1.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++;
+ }
+ else if(!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;
+ }
+ else if(!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;
+ else if($delta>$max)
+ $max=$delta;
+ $calls++;
+ $total+=$delta;
+ return array($token,$calls,$min,$max,$total);
+ }
+} \ No newline at end of file