Commit 83a45ad1 by Qiang Xue

Improved debug backtrace tracking.

parent 52e5a5dd
...@@ -26,12 +26,6 @@ defined('YII_DEBUG') or define('YII_DEBUG', false); ...@@ -26,12 +26,6 @@ defined('YII_DEBUG') or define('YII_DEBUG', false);
*/ */
defined('YII_ENV') or define('YII_ENV', 'prod'); defined('YII_ENV') or define('YII_ENV', 'prod');
/** /**
* This constant defines how much call stack information (file name and line number) should be logged by Yii::trace().
* Defaults to 0, meaning no backtrace information. If it is greater than 0,
* at most that number of call stacks will be logged. Note, only user application call stacks are considered.
*/
defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL', 0);
/**
* This constant defines the framework installation directory. * This constant defines the framework installation directory.
*/ */
defined('YII_PATH') or define('YII_PATH', __DIR__); defined('YII_PATH') or define('YII_PATH', __DIR__);
......
...@@ -36,3 +36,10 @@ ...@@ -36,3 +36,10 @@
span.indent { span.indent {
color: #ccc; color: #ccc;
} }
ul.trace {
font-size: 12px;
color: #666;
margin: 2px 0 0 20px;
}
...@@ -52,19 +52,28 @@ EOD; ...@@ -52,19 +52,28 @@ EOD;
{ {
$rows = array(); $rows = array();
foreach ($this->data['messages'] as $log) { foreach ($this->data['messages'] as $log) {
$time = date('H:i:s.', $log[3]) . sprintf('%03d', (int)(($log[3] - (int)$log[3]) * 1000)); list ($message, $level, $category, $time, $traces) = $log;
$level = Logger::getLevelName($log[1]); $time = date('H:i:s.', $time) . sprintf('%03d', (int)(($time - (int)$time) * 1000));
$message = Html::encode(wordwrap($log[0])); $message = Html::encode($message);
if ($log[1] == Logger::LEVEL_ERROR) { if (!empty($traces)) {
$message .= Html::ul($traces, array(
'class' => 'trace',
'item' => function ($trace) {
return "<li>{$trace['file']}({$trace['line']})</li>";
},
));
}
if ($level == Logger::LEVEL_ERROR) {
$class = ' class="error"'; $class = ' class="error"';
} elseif ($log[1] == Logger::LEVEL_WARNING) { } elseif ($level == Logger::LEVEL_WARNING) {
$class = ' class="warning"'; $class = ' class="warning"';
} elseif ($log[1] == Logger::LEVEL_INFO) { } elseif ($level == Logger::LEVEL_INFO) {
$class = ' class="info"'; $class = ' class="info"';
} else { } else {
$class = ''; $class = '';
} }
$rows[] = "<tr$class><td style=\"width: 100px;\">$time</td><td style=\"width: 100px;\">$level</td><td style=\"width: 250px;\">{$log[2]}</td><td>$message</td></tr>"; $level = Logger::getLevelName($level);
$rows[] = "<tr$class><td style=\"width: 100px;\">$time</td><td style=\"width: 100px;\">$level</td><td style=\"width: 250px;\">$category</td><td>$message</td></tr>";
} }
$rows = implode("\n", $rows); $rows = implode("\n", $rows);
return <<<EOD return <<<EOD
......
...@@ -111,6 +111,7 @@ class Logger extends Component ...@@ -111,6 +111,7 @@ class Logger extends Component
* [1] => level (integer) * [1] => level (integer)
* [2] => category (string) * [2] => category (string)
* [3] => timestamp (float, obtained by microtime(true)) * [3] => timestamp (float, obtained by microtime(true))
* [4] => traces (array, debug backtrace, contains the application code call stacks)
* ) * )
* ~~~ * ~~~
*/ */
...@@ -133,6 +134,12 @@ class Logger extends Component ...@@ -133,6 +134,12 @@ class Logger extends Component
* A smaller value means less memory, but will increase the execution time due to the overhead of [[flush()]]. * A smaller value means less memory, but will increase the execution time due to the overhead of [[flush()]].
*/ */
public $flushInterval = 1000; public $flushInterval = 1000;
/**
* @var integer how much call stack information (file name and line number) should be logged for each message.
* Defaults to 0, meaning no backtrace information. If it is greater than 0,
* at most that number of call stacks will be logged. Only application call stacks are considered.
*/
public $traceLevel = 0;
/** /**
* Initializes the logger by registering [[flush()]] as a shutdown function. * Initializes the logger by registering [[flush()]] as a shutdown function.
...@@ -150,8 +157,8 @@ class Logger extends Component ...@@ -150,8 +157,8 @@ class Logger extends Component
/** /**
* Logs a message with the given type and category. * Logs a message with the given type and category.
* If `YII_DEBUG` is true and `YII_TRACE_LEVEL` is greater than 0, then additional * If [[traceLevel]] is greater than 0, additional call stack information about
* call stack information about application code will be appended to the message. * the application code will be logged as well.
* @param string $message the message to be logged. * @param string $message the message to be logged.
* @param integer $level the level of the message. This must be one of the following: * @param integer $level the level of the message. This must be one of the following:
* `Logger::LEVEL_ERROR`, `Logger::LEVEL_WARNING`, `Logger::LEVEL_INFO`, `Logger::LEVEL_TRACE`, * `Logger::LEVEL_ERROR`, `Logger::LEVEL_WARNING`, `Logger::LEVEL_INFO`, `Logger::LEVEL_TRACE`,
...@@ -161,19 +168,22 @@ class Logger extends Component ...@@ -161,19 +168,22 @@ class Logger extends Component
public function log($message, $level, $category = 'application') public function log($message, $level, $category = 'application')
{ {
$time = microtime(true); $time = microtime(true);
if (YII_DEBUG && YII_TRACE_LEVEL > 0 && !($level & self::LEVEL_PROFILE)) { $traces = array();
$traces = debug_backtrace(); if ($this->traceLevel > 0) {
$count = 0; $count = 0;
foreach ($traces as $trace) { $ts = debug_backtrace();
array_pop($ts); // remove the last trace since it would be the entry script, not very useful
foreach ($ts as $trace) {
if (isset($trace['file'], $trace['line']) && strpos($trace['file'], YII_PATH) !== 0) { if (isset($trace['file'], $trace['line']) && strpos($trace['file'], YII_PATH) !== 0) {
$message .= "\nin {$trace['file']} ({$trace['line']})"; unset($trace['object'], $trace['args']);
if (++$count >= YII_TRACE_LEVEL) { $traces[] = $trace;
if (++$count >= $this->traceLevel) {
break; break;
} }
} }
} }
} }
$this->messages[] = array($message, $level, $category, $time); $this->messages[] = array($message, $level, $category, $time, $traces);
if ($this->flushInterval > 0 && count($this->messages) >= $this->flushInterval) { if ($this->flushInterval > 0 && count($this->messages) >= $this->flushInterval) {
$this->flush(); $this->flush();
} }
......
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