Qiang Xue
14 years ago
10 changed files with 1528 additions and 70 deletions
@ -0,0 +1,156 @@
|
||||
<?php |
||||
/** |
||||
* CDbLogRoute class file. |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright © 2008-2011 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
|
||||
/** |
||||
* CDbLogRoute stores log messages in a database table. |
||||
* |
||||
* To specify the database table for storing log messages, set {@link logTableName} as |
||||
* the name of the table and specify {@link connectionID} to be the ID of a {@link CDbConnection} |
||||
* application component. If they are not set, a SQLite3 database named 'log-YiiVersion.db' will be created |
||||
* and used under the application runtime directory. |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @version $Id: CDbLogRoute.php 3069 2011-03-14 00:28:38Z qiang.xue $ |
||||
* @package system.logging |
||||
* @since 1.0 |
||||
*/ |
||||
class CDbLogRoute extends CLogRoute |
||||
{ |
||||
/** |
||||
* @var string the ID of CDbConnection application component. If not set, a SQLite database |
||||
* will be automatically created and used. The SQLite database file is |
||||
* <code>protected/runtime/log-YiiVersion.db</code>. |
||||
*/ |
||||
public $connectionID; |
||||
/** |
||||
* @var string the name of the DB table that stores log content. Defaults to 'YiiLog'. |
||||
* If {@link autoCreateLogTable} is false and you want to create the DB table manually by yourself, |
||||
* you need to make sure the DB table is of the following structure: |
||||
* <pre> |
||||
* ( |
||||
* id INTEGER NOT NULL PRIMARY KEY, |
||||
* level VARCHAR(128), |
||||
* category VARCHAR(128), |
||||
* logtime INTEGER, |
||||
* message TEXT |
||||
* ) |
||||
* </pre> |
||||
* Note, the 'id' column must be created as an auto-incremental column. |
||||
* In MySQL, this means it should be <code>id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY</code>; |
||||
* In PostgreSQL, it is <code>id SERIAL PRIMARY KEY</code>. |
||||
* @see autoCreateLogTable |
||||
*/ |
||||
public $logTableName = 'YiiLog'; |
||||
/** |
||||
* @var boolean whether the log DB table should be automatically created if not exists. Defaults to true. |
||||
* @see logTableName |
||||
*/ |
||||
public $autoCreateLogTable = true; |
||||
/** |
||||
* @var CDbConnection the DB connection instance |
||||
*/ |
||||
private $_db; |
||||
|
||||
/** |
||||
* Initializes the route. |
||||
* This method is invoked after the route is created by the route manager. |
||||
*/ |
||||
public function init() |
||||
{ |
||||
parent::init(); |
||||
|
||||
if ($this->autoCreateLogTable) |
||||
{ |
||||
$db = $this->getDbConnection(); |
||||
$sql = "DELETE FROM {$this->logTableName} WHERE 0=1"; |
||||
try |
||||
{ |
||||
$db->createCommand($sql)->execute(); |
||||
} |
||||
catch(Exception $e) |
||||
{ |
||||
$this->createLogTable($db, $this->logTableName); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Creates the DB table for storing log messages. |
||||
* @param CDbConnection $db the database connection |
||||
* @param string $tableName the name of the table to be created |
||||
*/ |
||||
protected function createLogTable($db, $tableName) |
||||
{ |
||||
$driver = $db->getDriverName(); |
||||
if ($driver === 'mysql') |
||||
$logID = 'id INTEGER NOT NULL AUTO_INCREMENT PRIMARY KEY'; |
||||
elseif ($driver === 'pgsql') |
||||
$logID = 'id SERIAL PRIMARY KEY'; |
||||
else |
||||
$logID = 'id INTEGER NOT NULL PRIMARY KEY'; |
||||
|
||||
$sql = " |
||||
CREATE TABLE $tableName |
||||
( |
||||
$logID, |
||||
level VARCHAR(128), |
||||
category VARCHAR(128), |
||||
logtime INTEGER, |
||||
message TEXT |
||||
)"; |
||||
$db->createCommand($sql)->execute(); |
||||
} |
||||
|
||||
/** |
||||
* @return CDbConnection the DB connection instance |
||||
* @throws CException if {@link connectionID} does not point to a valid application component. |
||||
*/ |
||||
protected function getDbConnection() |
||||
{ |
||||
if ($this->_db !== null) |
||||
return $this->_db; |
||||
elseif (($id = $this->connectionID) !== null) |
||||
{ |
||||
if (($this->_db = Yii::app()->getComponent($id)) instanceof CDbConnection) |
||||
return $this->_db; |
||||
else |
||||
throw new CException(Yii::t('yii', 'CDbLogRoute.connectionID "{id}" does not point to a valid CDbConnection application component.', |
||||
array('{id}' => $id))); |
||||
} |
||||
else |
||||
{ |
||||
$dbFile = Yii::app()->getRuntimePath() . DIRECTORY_SEPARATOR . 'log-' . Yii::getVersion() . '.db'; |
||||
return $this->_db = new CDbConnection('sqlite:' . $dbFile); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Stores log messages into database. |
||||
* @param array $logs list of log messages |
||||
*/ |
||||
protected function processLogs($logs) |
||||
{ |
||||
$sql = " |
||||
INSERT INTO {$this->logTableName} |
||||
(level, category, logtime, message) VALUES |
||||
(:level, :category, :logtime, :message) |
||||
"; |
||||
$command = $this->getDbConnection()->createCommand($sql); |
||||
foreach ($logs as $log) |
||||
{ |
||||
$command->bindValue(':level', $log[1]); |
||||
$command->bindValue(':category', $log[2]); |
||||
$command->bindValue(':logtime', (int)$log[3]); |
||||
$command->bindValue(':message', $log[0]); |
||||
$command->execute(); |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,146 @@
|
||||
<?php |
||||
/** |
||||
* CEmailLogRoute class file. |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright © 2008-2011 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
/** |
||||
* CEmailLogRoute sends selected log messages to email addresses. |
||||
* |
||||
* The target email addresses may be specified via {@link setEmails emails} property. |
||||
* Optionally, you may set the email {@link setSubject subject}, the |
||||
* {@link setSentFrom sentFrom} address and any additional {@link setHeaders headers}. |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @version $Id: CEmailLogRoute.php 3001 2011-02-24 16:42:44Z alexander.makarow $ |
||||
* @package system.logging |
||||
* @since 1.0 |
||||
*/ |
||||
class CEmailLogRoute extends CLogRoute |
||||
{ |
||||
/** |
||||
* @var array list of destination email addresses. |
||||
*/ |
||||
private $_email = array(); |
||||
/** |
||||
* @var string email subject |
||||
*/ |
||||
private $_subject; |
||||
/** |
||||
* @var string email sent from address |
||||
*/ |
||||
private $_from; |
||||
/** |
||||
* @var array list of additional headers to use when sending an email. |
||||
*/ |
||||
private $_headers = array(); |
||||
|
||||
/** |
||||
* Sends log messages to specified email addresses. |
||||
* @param array $logs list of log messages |
||||
*/ |
||||
protected function processLogs($logs) |
||||
{ |
||||
$message = ''; |
||||
foreach ($logs as $log) |
||||
$message .= $this->formatLogMessage($log[0], $log[1], $log[2], $log[3]); |
||||
$message = wordwrap($message, 70); |
||||
$subject = $this->getSubject(); |
||||
if ($subject === null) |
||||
$subject = Yii::t('yii', 'Application Log'); |
||||
foreach ($this->getEmails() as $email) |
||||
$this->sendEmail($email, $subject, $message); |
||||
} |
||||
|
||||
/** |
||||
* Sends an email. |
||||
* @param string $email single email address |
||||
* @param string $subject email subject |
||||
* @param string $message email content |
||||
*/ |
||||
protected function sendEmail($email, $subject, $message) |
||||
{ |
||||
$headers = $this->getHeaders(); |
||||
if (($from = $this->getSentFrom()) !== null) |
||||
$headers[] = "From: {$from}"; |
||||
mail($email, $subject, $message, implode("\r\n", $headers)); |
||||
} |
||||
|
||||
/** |
||||
* @return array list of destination email addresses |
||||
*/ |
||||
public function getEmails() |
||||
{ |
||||
return $this->_email; |
||||
} |
||||
|
||||
/** |
||||
* @param mixed $value list of destination email addresses. If the value is |
||||
* a string, it is assumed to be comma-separated email addresses. |
||||
*/ |
||||
public function setEmails($value) |
||||
{ |
||||
if (is_array($value)) |
||||
$this->_email = $value; |
||||
else |
||||
$this->_email = preg_split('/[\s,]+/', $value, -1, PREG_SPLIT_NO_EMPTY); |
||||
} |
||||
|
||||
/** |
||||
* @return string email subject. Defaults to CEmailLogRoute::DEFAULT_SUBJECT |
||||
*/ |
||||
public function getSubject() |
||||
{ |
||||
return $this->_subject; |
||||
} |
||||
|
||||
/** |
||||
* @param string $value email subject. |
||||
*/ |
||||
public function setSubject($value) |
||||
{ |
||||
$this->_subject = $value; |
||||
} |
||||
|
||||
/** |
||||
* @return string send from address of the email |
||||
*/ |
||||
public function getSentFrom() |
||||
{ |
||||
return $this->_from; |
||||
} |
||||
|
||||
/** |
||||
* @param string $value send from address of the email |
||||
*/ |
||||
public function setSentFrom($value) |
||||
{ |
||||
$this->_from = $value; |
||||
} |
||||
|
||||
/** |
||||
* @return array additional headers to use when sending an email. |
||||
* @since 1.1.4 |
||||
*/ |
||||
public function getHeaders() |
||||
{ |
||||
return $this->_headers; |
||||
} |
||||
|
||||
/** |
||||
* @param mixed $value list of additional headers to use when sending an email. |
||||
* If the value is a string, it is assumed to be line break separated headers. |
||||
* @since 1.1.4 |
||||
*/ |
||||
public function setHeaders($value) |
||||
{ |
||||
if (is_array($value)) |
||||
$this->_headers = $value; |
||||
else |
||||
$this->_headers = preg_split('/\r\n|\n/', $value, -1, PREG_SPLIT_NO_EMPTY); |
||||
} |
||||
} |
@ -0,0 +1,167 @@
|
||||
<?php |
||||
/** |
||||
* CFileLogRoute class file. |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright © 2008-2011 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
/** |
||||
* CFileLogRoute records log messages in files. |
||||
* |
||||
* The log files are stored under {@link setLogPath logPath} and the file name |
||||
* is specified by {@link setLogFile logFile}. If the size of the log file is |
||||
* greater than {@link setMaxFileSize maxFileSize} (in kilo-bytes), a rotation |
||||
* is performed, which renames the current log file by suffixing the file name |
||||
* with '.1'. All existing log files are moved backwards one place, i.e., '.2' |
||||
* to '.3', '.1' to '.2'. The property {@link setMaxLogFiles maxLogFiles} |
||||
* specifies how many files to be kept. |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @version $Id: CFileLogRoute.php 3001 2011-02-24 16:42:44Z alexander.makarow $ |
||||
* @package system.logging |
||||
* @since 1.0 |
||||
*/ |
||||
class CFileLogRoute extends CLogRoute |
||||
{ |
||||
/** |
||||
* @var integer maximum log file size |
||||
*/ |
||||
private $_maxFileSize = 1024; // in KB |
||||
/** |
||||
* @var integer number of log files used for rotation |
||||
*/ |
||||
private $_maxLogFiles = 5; |
||||
/** |
||||
* @var string directory storing log files |
||||
*/ |
||||
private $_logPath; |
||||
/** |
||||
* @var string log file name |
||||
*/ |
||||
private $_logFile = 'application.log'; |
||||
|
||||
|
||||
/** |
||||
* Initializes the route. |
||||
* This method is invoked after the route is created by the route manager. |
||||
*/ |
||||
public function init() |
||||
{ |
||||
parent::init(); |
||||
if ($this->getLogPath() === null) |
||||
$this->setLogPath(Yii::app()->getRuntimePath()); |
||||
} |
||||
|
||||
/** |
||||
* @return string directory storing log files. Defaults to application runtime path. |
||||
*/ |
||||
public function getLogPath() |
||||
{ |
||||
return $this->_logPath; |
||||
} |
||||
|
||||
/** |
||||
* @param string $value directory for storing log files. |
||||
* @throws CException if the path is invalid |
||||
*/ |
||||
public function setLogPath($value) |
||||
{ |
||||
$this->_logPath = realpath($value); |
||||
if ($this->_logPath === false || !is_dir($this->_logPath) || !is_writable($this->_logPath)) |
||||
throw new CException(Yii::t('yii', 'CFileLogRoute.logPath "{path}" does not point to a valid directory. Make sure the directory exists and is writable by the Web server process.', |
||||
array('{path}' => $value))); |
||||
} |
||||
|
||||
/** |
||||
* @return string log file name. Defaults to 'application.log'. |
||||
*/ |
||||
public function getLogFile() |
||||
{ |
||||
return $this->_logFile; |
||||
} |
||||
|
||||
/** |
||||
* @param string $value log file name |
||||
*/ |
||||
public function setLogFile($value) |
||||
{ |
||||
$this->_logFile = $value; |
||||
} |
||||
|
||||
/** |
||||
* @return integer maximum log file size in kilo-bytes (KB). Defaults to 1024 (1MB). |
||||
*/ |
||||
public function getMaxFileSize() |
||||
{ |
||||
return $this->_maxFileSize; |
||||
} |
||||
|
||||
/** |
||||
* @param integer $value maximum log file size in kilo-bytes (KB). |
||||
*/ |
||||
public function setMaxFileSize($value) |
||||
{ |
||||
if (($this->_maxFileSize = (int)$value) < 1) |
||||
$this->_maxFileSize = 1; |
||||
} |
||||
|
||||
/** |
||||
* @return integer number of files used for rotation. Defaults to 5. |
||||
*/ |
||||
public function getMaxLogFiles() |
||||
{ |
||||
return $this->_maxLogFiles; |
||||
} |
||||
|
||||
/** |
||||
* @param integer $value number of files used for rotation. |
||||
*/ |
||||
public function setMaxLogFiles($value) |
||||
{ |
||||
if (($this->_maxLogFiles = (int)$value) < 1) |
||||
$this->_maxLogFiles = 1; |
||||
} |
||||
|
||||
/** |
||||
* Saves log messages in files. |
||||
* @param array $logs list of log messages |
||||
*/ |
||||
protected function processLogs($logs) |
||||
{ |
||||
$logFile = $this->getLogPath() . DIRECTORY_SEPARATOR . $this->getLogFile(); |
||||
if (@filesize($logFile) > $this->getMaxFileSize() * 1024) |
||||
$this->rotateFiles(); |
||||
$fp = @fopen($logFile, 'a'); |
||||
@flock($fp, LOCK_EX); |
||||
foreach ($logs as $log) |
||||
@fwrite($fp, $this->formatLogMessage($log[0], $log[1], $log[2], $log[3])); |
||||
@flock($fp, LOCK_UN); |
||||
@fclose($fp); |
||||
} |
||||
|
||||
/** |
||||
* Rotates log files. |
||||
*/ |
||||
protected function rotateFiles() |
||||
{ |
||||
$file = $this->getLogPath() . DIRECTORY_SEPARATOR . $this->getLogFile(); |
||||
$max = $this->getMaxLogFiles(); |
||||
for ($i = $max;$i > 0;--$i) |
||||
{ |
||||
$rotateFile = $file . '.' . $i; |
||||
if (is_file($rotateFile)) |
||||
{ |
||||
// suppress errors because it's possible multiple processes enter into this section |
||||
if ($i === $max) |
||||
@unlink($rotateFile); |
||||
else |
||||
@rename($rotateFile, $file . '.' . ($i + 1)); |
||||
} |
||||
} |
||||
if (is_file($file)) |
||||
@rename($file, $file . '.1'); // suppress errors because it's possible multiple processes enter into this section |
||||
} |
||||
} |
@ -0,0 +1,107 @@
|
||||
<?php |
||||
/** |
||||
* CLogFilter class file |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright © 2008-2011 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
/** |
||||
* CLogFilter preprocesses the logged messages before they are handled by a log route. |
||||
* |
||||
* CLogFilter is meant to be used by a log route to preprocess the logged messages |
||||
* before they are handled by the route. The default implementation of CLogFilter |
||||
* prepends additional context information to the logged messages. In particular, |
||||
* by setting {@link logVars}, predefined PHP variables such as |
||||
* $_SERVER, $_POST, etc. can be saved as a log message, which may help identify/debug |
||||
* issues encountered. |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @version $Id: CLogFilter.php 3204 2011-05-05 21:36:32Z alexander.makarow $ |
||||
* @package system.logging |
||||
* @since 1.0.6 |
||||
*/ |
||||
class CLogFilter extends CComponent |
||||
{ |
||||
/** |
||||
* @var boolean whether to prefix each log message with the current user session ID. |
||||
* Defaults to false. |
||||
*/ |
||||
public $prefixSession = false; |
||||
/** |
||||
* @var boolean whether to prefix each log message with the current user |
||||
* {@link CWebUser::name name} and {@link CWebUser::id ID}. Defaults to false. |
||||
*/ |
||||
public $prefixUser = false; |
||||
/** |
||||
* @var boolean whether to log the current user name and ID. Defaults to true. |
||||
*/ |
||||
public $logUser = true; |
||||
/** |
||||
* @var array list of the PHP predefined variables that should be logged. |
||||
* Note that a variable must be accessible via $GLOBALS. Otherwise it won't be logged. |
||||
*/ |
||||
public $logVars = array('_GET', '_POST', '_FILES', '_COOKIE', '_SESSION', '_SERVER'); |
||||
|
||||
|
||||
/** |
||||
* Filters the given log messages. |
||||
* This is the main method of CLogFilter. It processes the log messages |
||||
* by adding context information, etc. |
||||
* @param array $logs the log messages |
||||
* @return array |
||||
*/ |
||||
public function filter(&$logs) |
||||
{ |
||||
if (!empty($logs)) |
||||
{ |
||||
if (($message = $this->getContext()) !== '') |
||||
array_unshift($logs, array($message, CLogger::LEVEL_INFO, 'application', YII_BEGIN_TIME)); |
||||
$this->format($logs); |
||||
} |
||||
return $logs; |
||||
} |
||||
|
||||
/** |
||||
* Formats the log messages. |
||||
* The default implementation will prefix each message with session ID |
||||
* if {@link prefixSession} is set true. It may also prefix each message |
||||
* with the current user's name and ID if {@link prefixUser} is true. |
||||
* @param array $logs the log messages |
||||
*/ |
||||
protected function format(&$logs) |
||||
{ |
||||
$prefix = ''; |
||||
if ($this->prefixSession && ($id = session_id()) !== '') |
||||
$prefix .= "[$id]"; |
||||
if ($this->prefixUser && ($user = Yii::app()->getComponent('user', false)) !== null) |
||||
$prefix .= '[' . $user->getName() . '][' . $user->getId() . ']'; |
||||
if ($prefix !== '') |
||||
{ |
||||
foreach ($logs as &$log) |
||||
$log[0] = $prefix . ' ' . $log[0]; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Generates the context information to be logged. |
||||
* The default implementation will dump user information, system variables, etc. |
||||
* @return string the context information. If an empty string, it means no context information. |
||||
*/ |
||||
protected function getContext() |
||||
{ |
||||
$context = array(); |
||||
if ($this->logUser && ($user = Yii::app()->getComponent('user', false)) !== null) |
||||
$context[] = 'User: ' . $user->getName() . ' (ID: ' . $user->getId() . ')'; |
||||
|
||||
foreach ($this->logVars as $name) |
||||
{ |
||||
if (!empty($GLOBALS[$name])) |
||||
$context[] = "\$ {$name}=" . var_export($GLOBALS[$name], true); |
||||
} |
||||
|
||||
return implode("\n\n", $context); |
||||
} |
||||
} |
@ -0,0 +1,320 @@
|
||||
<?php |
||||
/** |
||||
* Logger class file |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright © 2008-2012 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
namespace yii\logging; |
||||
|
||||
/** |
||||
* Logger records logged messages in memory. |
||||
* |
||||
* When [[flushInterval]] is reached or when application terminates, it will |
||||
* call [[flush]] to send logged messages to different log targets, such as |
||||
* file, email, Web. |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @since 2.0 |
||||
*/ |
||||
class Logger extends \yii\base\Component |
||||
{ |
||||
const LEVEL_TRACE = 'trace'; |
||||
const LEVEL_WARN = 'warn'; |
||||
const LEVEL_ERROR = 'error'; |
||||
const LEVEL_INFO = 'info'; |
||||
const LEVEL_PROFILE = 'profile'; |
||||
|
||||
/** |
||||
* @var integer how many messages should be logged before they are flushed from memory and sent to targets. |
||||
* Defaults to 1000, meaning the [[flush]] method will be invoked once every 1000 messages logged. |
||||
* Set this property to be 0 if you don't want to flush messages until the application terminates. |
||||
* This property mainly affects how much memory will be taken by the logged messages. |
||||
* A smaller value means less memory, but will increase the execution time due to the overhead of [[flush]]. |
||||
*/ |
||||
public $flushInterval = 1000; |
||||
/** |
||||
* @var boolean this property will be passed as the parameter to [[flush]] when it is |
||||
* called due to the [[flushInterval]] is reached. Defaults to false, meaning the flushed |
||||
* messages are still kept in the memory by each log target. If this is true, they will |
||||
* be exported to the actual storage medium (e.g. DB, email) defined by each log target. |
||||
* @see flushInterval |
||||
*/ |
||||
public $autoExport = false; |
||||
/** |
||||
* @var array logged messages. This property is mainly managed by [[log]] and [[flush]]. |
||||
*/ |
||||
public $messages = array(); |
||||
/** |
||||
* @var array the profiling results (category, token => time in seconds) |
||||
*/ |
||||
private $_timings; |
||||
|
||||
/** |
||||
* Logs an error message. |
||||
* An error message is typically logged when an unrecoverable error occurs |
||||
* during the execution of an application. |
||||
* @param string $message the message to be logged. |
||||
* @param string $category the category of the message. |
||||
*/ |
||||
public function error($message, $category = 'application') |
||||
{ |
||||
$this->log($message, self::LEVEL_ERROR, $category); |
||||
} |
||||
|
||||
/** |
||||
* Logs a trace message. |
||||
* Trace messages are logged mainly for development purpose to see |
||||
* the execution work flow of some code. |
||||
* @param string $message the message to be logged. |
||||
* @param string $category the category of the message. |
||||
*/ |
||||
public function trace($message, $category = 'application') |
||||
{ |
||||
$this->log($message, self::LEVEL_TRACE, $category); |
||||
} |
||||
|
||||
/** |
||||
* Logs a warning message. |
||||
* A warning message is typically logged when an error occurs while the execution |
||||
* can still continue. |
||||
* @param string $message the message to be logged. |
||||
* @param string $category the category of the message. |
||||
*/ |
||||
public function warn($message, $category = 'application') |
||||
{ |
||||
$this->log($message, self::LEVEL_TRACE, $category); |
||||
} |
||||
|
||||
/** |
||||
* Logs an informative message. |
||||
* An informative message is typically logged by an application to keep record of |
||||
* something important (e.g. an administrator logs in). |
||||
* @param string $message the message to be logged. |
||||
* @param string $category the category of the message. |
||||
*/ |
||||
public function info($message, $category = 'application') |
||||
{ |
||||
$this->log($message, self::LEVEL_TRACE, $category); |
||||
} |
||||
|
||||
/** |
||||
* Marks the beginning of a code block for profiling. |
||||
* This has to be matched with a call to [[endProfile]] with the same category name. |
||||
* The begin- and end- calls must also be properly nested. For example, |
||||
* @param string $category the category of this profile block |
||||
* @see endProfile |
||||
*/ |
||||
public function beginProfile($category) |
||||
{ |
||||
$this->log('begin', self::LEVEL_PROFILE, $category); |
||||
} |
||||
|
||||
/** |
||||
* Marks the end of a code block for profiling. |
||||
* This has to be matched with a previous call to [[beginProfile]] with the same category name. |
||||
* @param string $category the category of this profile block |
||||
* @see beginProfile |
||||
*/ |
||||
public function endProfile($category) |
||||
{ |
||||
$this->log('end', self::LEVEL_PROFILE, $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 |
||||
* call stack information about application code will be appended to the message. |
||||
* @param string $message the message to be logged. |
||||
* @param string $level the level of the message. This must be one of the following: |
||||
* 'trace', 'info', 'warn', 'error', 'profile'. |
||||
* @param string $category the category of the message. |
||||
*/ |
||||
public function log($message, $level, $category) |
||||
{ |
||||
if (YII_DEBUG && YII_TRACE_LEVEL > 0 && $level !== self::LEVEL_PROFILE) { |
||||
$traces = debug_backtrace(); |
||||
$count = 0; |
||||
foreach ($traces as $trace) { |
||||
if (isset($trace['file'], $trace['line']) && strpos($trace['file'], YII_DIR) !== 0) { |
||||
$message .= "\nin " . $trace['file'] . ' (' . $trace['line'] . ')'; |
||||
if (++$count >= YII_TRACE_LEVEL) { |
||||
break; |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
$this->messages[] = array($message, $level, $category, microtime(true)); |
||||
if (count($this->messages) >= $this->flushInterval && $this->flushInterval > 0) { |
||||
$this->flush($this->autoExport); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Retrieves log messages. |
||||
* |
||||
* Messages may be filtered by log levels and/or categories. |
||||
* A level filter is specified by a list of levels separated by comma or space |
||||
* (e.g. 'trace, error'). A category filter is similar to level filter |
||||
* (e.g. 'system, system.web'). A difference is that in category filter |
||||
* you can use pattern like 'system.*' to indicate all categories starting |
||||
* with 'system'. |
||||
* |
||||
* If you do not specify level filter, it will bring back logs at all levels. |
||||
* The same applies to category filter. |
||||
* |
||||
* Level filter and category filter are combinational, i.e., only messages |
||||
* satisfying both filter conditions will be returned. |
||||
* |
||||
* @param string $levels level filter |
||||
* @param string $categories category filter |
||||
* @return array list of messages. Each array elements represents one message |
||||
* with the following structure: |
||||
* array( |
||||
* [0] => message (string) |
||||
* [1] => level (string) |
||||
* [2] => category (string) |
||||
* [3] => timestamp (float, obtained by microtime(true)); |
||||
*/ |
||||
public function getLogs($levels = '', $categories = '') |
||||
{ |
||||
$this->_levels = preg_split('/[\s,]+/', strtolower($levels), -1, PREG_SPLIT_NO_EMPTY); |
||||
$this->_categories = preg_split('/[\s,]+/', strtolower($categories), -1, PREG_SPLIT_NO_EMPTY); |
||||
if (empty($levels) && empty($categories)) |
||||
return $this->_logs; |
||||
elseif (empty($levels)) |
||||
return array_values(array_filter(array_filter($this->_logs, array($this, 'filterByCategory')))); |
||||
elseif (empty($categories)) |
||||
return array_values(array_filter(array_filter($this->_logs, array($this, 'filterByLevel')))); |
||||
else |
||||
{ |
||||
$ret = array_values(array_filter(array_filter($this->_logs, array($this, 'filterByLevel')))); |
||||
return array_values(array_filter(array_filter($ret, array($this, 'filterByCategory')))); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Filter function used by {@link getLogs} |
||||
* @param array $value element to be filtered |
||||
* @return array valid log, false if not. |
||||
*/ |
||||
private function filterByCategory($value) |
||||
{ |
||||
foreach ($this->_categories as $category) |
||||
{ |
||||
$cat = strtolower($value[2]); |
||||
if ($cat === $category || (($c = rtrim($category, '.*')) !== $category && strpos($cat, $c) === 0)) |
||||
return $value; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* Filter function used by {@link getLogs} |
||||
* @param array $value element to be filtered |
||||
* @return array valid log, false if not. |
||||
*/ |
||||
private function filterByLevel($value) |
||||
{ |
||||
return in_array(strtolower($value[1]), $this->_levels) ? $value : false; |
||||
} |
||||
|
||||
/** |
||||
* Returns the total elapsed time since the start of the current request. |
||||
* This method calculates the difference between now and the timestamp |
||||
* defined by constant `YII_BEGIN_TIME` which is evaluated at the beginning |
||||
* of [[YiiBase]] class file. |
||||
* @return float the total elapsed time in seconds for current request. |
||||
*/ |
||||
public function getExecutionTime() |
||||
{ |
||||
return microtime(true) - YII_BEGIN_TIME; |
||||
} |
||||
|
||||
/** |
||||
* Returns the profiling results. |
||||
* The results may be filtered by token and/or category. |
||||
* If no filter is specified, the returned results would be an array with each element |
||||
* being `array($token, $category, $time)`. |
||||
* If a filter is specified, the results would be an array of timings. |
||||
* @param string $token token filter. Defaults to null, meaning not filtered by token. |
||||
* @param string $category category filter. Defaults to null, meaning not filtered by category. |
||||
* @param boolean $refresh whether to refresh the internal timing calculations. If false, |
||||
* only the first time calling this method will the timings be calculated internally. |
||||
* @return array the profiling results. |
||||
*/ |
||||
public function getProfilingResults($token = null, $category = null, $refresh = false) |
||||
{ |
||||
if ($this->_timings === null || $refresh) { |
||||
$this->calculateTimings(); |
||||
} |
||||
if ($token === null && $category === null) { |
||||
return $this->_timings; |
||||
} |
||||
$results = array(); |
||||
foreach ($this->_timings as $timing) { |
||||
if (($category === null || $timing[1] === $category) && ($token === null || $timing[0] === $token)) { |
||||
$results[] = $timing[2]; |
||||
} |
||||
} |
||||
return $results; |
||||
} |
||||
|
||||
private function calculateTimings() |
||||
{ |
||||
$this->_timings = array(); |
||||
|
||||
$stack = array(); |
||||
foreach ($this->messages as $log) { |
||||
if ($log[1] !== self::LEVEL_PROFILE) { |
||||
continue; |
||||
} |
||||
list($message, $level, $category, $timestamp) = $log; |
||||
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]; |
||||
$this->_timings[] = array($message, $category, $delta); |
||||
} |
||||
else { |
||||
throw new \yii\base\Exception('Found a mismatching profiling block: ' . $token); |
||||
} |
||||
} |
||||
} |
||||
|
||||
$now = microtime(true); |
||||
while (($last = array_pop($stack)) !== null) { |
||||
$delta = $now - $last[3]; |
||||
$this->_timings[] = array($last[0], $last[2], $delta); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Removes all recorded messages from the memory. |
||||
* This method will raise an {@link onFlush} event. |
||||
* The attached event handlers can process the log messages before they are removed. |
||||
* @param boolean $export whether to notify log targets to export the filtered messages they have received. |
||||
*/ |
||||
public function flush($export = false) |
||||
{ |
||||
$this->onFlush(new \yii\base\Event($this, array('export' => $export))); |
||||
$this->messages = array(); |
||||
} |
||||
|
||||
/** |
||||
* Raises an `onFlush` event. |
||||
* @param \yii\base\Event $event the event parameter |
||||
*/ |
||||
public function onFlush($event) |
||||
{ |
||||
$this->raiseEvent('onFlush', $event); |
||||
} |
||||
} |
@ -0,0 +1,201 @@
|
||||
<?php |
||||
/** |
||||
* CProfileLogRoute class file. |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @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: |
||||
* <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> |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @version $Id: CProfileLogRoute.php 3204 2011-05-05 21:36:32Z alexander.makarow $ |
||||
* @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'. |
||||
* @since 1.0.6 |
||||
*/ |
||||
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); |
||||
} |
||||
} |
@ -0,0 +1,136 @@
|
||||
<?php |
||||
/** |
||||
* Router class file. |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright © 2008-2012 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
namespace yii\logging; |
||||
|
||||
/** |
||||
* Router manages [[Target|log targets]] that record log messages in different media. |
||||
* |
||||
* For example, a [[FileTarget|file log target]] records log messages |
||||
* in files; an [[EmailTarget|email log target]] sends log messages |
||||
* to specific email addresses. Each log target may specify filters on |
||||
* message levels and categories to record specific messages only. |
||||
* |
||||
* Router and the targets it manages may be configured in application configuration, |
||||
* like the following: |
||||
* |
||||
* ~~~ |
||||
* array( |
||||
* // preload log component when application starts |
||||
* 'preload' => array('log'), |
||||
* 'components' => array( |
||||
* 'log' => array( |
||||
* 'class' => '\yii\logging\Router', |
||||
* 'targets' => array( |
||||
* 'file' => array( |
||||
* 'class' => '\yii\logging\FileTarget', |
||||
* 'levels' => 'trace, info', |
||||
* 'categories' => 'yii\*', |
||||
* ), |
||||
* 'email' => array( |
||||
* 'class' => '\yii\logging\EmailTarget', |
||||
* 'levels' => 'error, warning', |
||||
* 'emails' => array('admin@example.com'), |
||||
* ), |
||||
* ), |
||||
* ), |
||||
* ), |
||||
* ) |
||||
* ~~~ |
||||
* |
||||
* Each log target can have a name and can be referenced via the [[targets]] property |
||||
* as follows: |
||||
* |
||||
* ~~~ |
||||
* \Yii::app()->log->targets['file']->enabled = false; |
||||
* ~~~ |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @since 2.0 |
||||
*/ |
||||
class Router extends \yii\base\ApplicationComponent |
||||
{ |
||||
private $_targets; |
||||
|
||||
/** |
||||
* Constructor. |
||||
*/ |
||||
public function __construct() |
||||
{ |
||||
$this->_targets = new \yii\base\Dictionary; |
||||
} |
||||
|
||||
/** |
||||
* Initializes this application component. |
||||
* This method is invoked when the Router component is created by the application. |
||||
* The method attaches the [[processLogs]] method to both the [[Logger::onFlush]] event |
||||
* and the [[\yii\base\Application::onEndRequest]] event. |
||||
*/ |
||||
public function init() |
||||
{ |
||||
parent::init(); |
||||
\Yii::getLogger()->attachEventHandler('onFlush', array($this, 'processMessages')); |
||||
if (($app = \Yii::app()) !== null) { |
||||
$app->attachEventHandler('onEndRequest', array($this, 'processMessages')); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Returns the log targets managed by this log router. |
||||
* The keys of the dictionary are the names of the log targets. |
||||
* You can use the name to access a specific log target. For example, |
||||
* |
||||
* ~~~ |
||||
* $target = $router->targets['file']; |
||||
* ~~~ |
||||
* @return \yii\base\Dictionary the targets managed by this log router. |
||||
*/ |
||||
public function getTargets() |
||||
{ |
||||
return $this->_targets; |
||||
} |
||||
|
||||
/** |
||||
* Sets the log targets. |
||||
* @param array $config list of log target configurations. Each array element |
||||
* represents the configuration for creating a single log target. It will be |
||||
* passed to [[\Yii::createComponent]] to create the target instance. |
||||
*/ |
||||
public function setTargets($config) |
||||
{ |
||||
foreach ($config as $name => $target) { |
||||
if ($target instanceof Target) { |
||||
$this->_targets[$name] = $target; |
||||
} |
||||
else { |
||||
$this->_targets[$name] = \Yii::createComponent($target); |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Retrieves and processes log messages from the system logger. |
||||
* This method mainly serves the event handler to [[Logger::onFlush]] |
||||
* and [[\yii\base\Application::onEndRequest]] events. |
||||
* It will retrieve the available log messages from the [[\Yii::getLogger|system logger]] |
||||
* and invoke the registered [[targets|log targets]] to do the actual processing. |
||||
* @param \yii\base\Event $event event parameter |
||||
*/ |
||||
public function processMessages($event) |
||||
{ |
||||
$logger = Yii::getLogger(); |
||||
$export = !isset($event->params['export']) || $event->params['export']; |
||||
foreach ($this->_targets as $target) { |
||||
if ($target->enabled) { |
||||
$target->processMessages($logger, $export); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,171 @@
|
||||
<?php |
||||
/** |
||||
* Target class file. |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright © 2008-2012 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
namespace yii\logging; |
||||
|
||||
/** |
||||
* Target is the base class for all log target classes. |
||||
* |
||||
* A log target object retrieves log messages from a logger and sends it |
||||
* somewhere, such as files, emails. |
||||
* The messages being retrieved may be filtered first before being sent |
||||
* to the destination. The filters include log level filter and log category filter. |
||||
* |
||||
* To specify level filter, set {@link levels} property, |
||||
* which takes a string of comma-separated desired level names (e.g. 'Error, Debug'). |
||||
* To specify category filter, set {@link categories} property, |
||||
* which takes a string of comma-separated desired category names (e.g. 'System.Web, System.IO'). |
||||
* |
||||
* Level filter and category filter are combinational, i.e., only messages |
||||
* satisfying both filter conditions will they be returned. |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @since 2.0 |
||||
*/ |
||||
abstract class Target extends \yii\base\Component implements \yii\base\Initable |
||||
{ |
||||
/** |
||||
* @var boolean whether to enable this log target. Defaults to true. |
||||
*/ |
||||
public $enabled = true; |
||||
/** |
||||
* @var string list of levels separated by comma or space. Defaults to empty, meaning all levels. |
||||
*/ |
||||
public $levels; |
||||
/** |
||||
* @var string list of categories separated by comma or space. Defaults to empty, meaning all categories. |
||||
*/ |
||||
public $categories; |
||||
/** |
||||
* @var string list of categories that should be excluded. |
||||
*/ |
||||
public $excludeCategories; |
||||
/** |
||||
* @var mixed the additional filter (eg {@link CLogFilter}) that can be applied to the log messages. |
||||
* The value of this property will be passed to {@link Yii::createComponent} to create |
||||
* a log filter object. As a result, this can be either a string representing the |
||||
* filter class name or an array representing the filter configuration. |
||||
* In general, the log filter class should be {@link CLogFilter} or a child class of it. |
||||
* Defaults to null, meaning no filter will be used. |
||||
*/ |
||||
public $filter; |
||||
/** |
||||
* @var array the messages that are collected so far by this log target. |
||||
*/ |
||||
public $messages; |
||||
|
||||
/** |
||||
* Pre-initializes this component. |
||||
* This method is required by the [[Initable]] interface. It is invoked by |
||||
* [[\Yii::createComponent]] after its creates the new component instance but |
||||
* BEFORE the component properties are initialized. |
||||
* |
||||
* You may override this method to do work such as setting property default values. |
||||
*/ |
||||
public function preinit() |
||||
{ |
||||
} |
||||
|
||||
/** |
||||
* Initializes this component. |
||||
* This method is invoked after the component is created and its property values are |
||||
* initialized. |
||||
*/ |
||||
public function init() |
||||
{ |
||||
} |
||||
|
||||
/** |
||||
* Formats a log message given different fields. |
||||
* @param string $message message content |
||||
* @param integer $level message level |
||||
* @param string $category message category |
||||
* @param integer $time timestamp |
||||
* @return string formatted message |
||||
*/ |
||||
protected function formatMessage($message, $level, $category, $time) |
||||
{ |
||||
return @date('Y/m/d H:i:s', $time) . " [$level] [$category] $message\n"; |
||||
} |
||||
|
||||
/** |
||||
* Retrieves filtered log messages from logger for further processing. |
||||
* @param CLogger $logger logger instance |
||||
* @param boolean $processLogs whether to process the messages after they are collected from the logger |
||||
*/ |
||||
public function processMessages($logger, $export) |
||||
{ |
||||
$messages = $logger->getLogs($this->levels, $this->categories); |
||||
$this->messages = empty($this->messages) ? $messages : array_merge($this->messages, $messages); |
||||
if ($processLogs && !empty($this->messages)) |
||||
{ |
||||
if ($this->filter !== null) |
||||
Yii::createComponent($this->filter)->filter($this->messages); |
||||
$this->processLogs($this->messages); |
||||
$this->messages = array(); |
||||
} |
||||
} |
||||
|
||||
protected function filterMessages($levels = '', $categories = '') |
||||
{ |
||||
$this->_levels = preg_split('/[\s,]+/', strtolower($levels), -1, PREG_SPLIT_NO_EMPTY); |
||||
$this->_categories = preg_split('/[\s,]+/', strtolower($categories), -1, PREG_SPLIT_NO_EMPTY); |
||||
if (empty($levels) && empty($categories)) |
||||
return $this->_logs; |
||||
elseif (empty($levels)) |
||||
return array_values(array_filter(array_filter($this->_logs, array($this, 'filterByCategory')))); |
||||
elseif (empty($categories)) |
||||
return array_values(array_filter(array_filter($this->_logs, array($this, 'filterByLevel')))); |
||||
else |
||||
{ |
||||
$ret = array_values(array_filter(array_filter($this->_logs, array($this, 'filterByLevel')))); |
||||
return array_values(array_filter(array_filter($ret, array($this, 'filterByCategory')))); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* Filter function used by {@link getLogs} |
||||
* @param array $value element to be filtered |
||||
* @return array valid log, false if not. |
||||
*/ |
||||
protected function filterByCategory($value) |
||||
{ |
||||
foreach ($this->_categories as $category) |
||||
{ |
||||
$cat = strtolower($value[2]); |
||||
if ($cat === $category || (($c = rtrim($category, '.*')) !== $category && strpos($cat, $c) === 0)) |
||||
return $value; |
||||
} |
||||
return false; |
||||
} |
||||
|
||||
/** |
||||
* Filter function used by {@link getLogs} |
||||
* @param array $value element to be filtered |
||||
* @return array valid log, false if not. |
||||
*/ |
||||
protected function filterByLevel($value) |
||||
{ |
||||
return in_array(strtolower($value[1]), $this->_levels) ? $value : false; |
||||
} |
||||
|
||||
/** |
||||
* Processes log messages and sends them to specific destination. |
||||
* Derived child classes must implement this method. |
||||
* @param array $messages list of messages. Each array elements represents one message |
||||
* with the following structure: |
||||
* array( |
||||
* [0] => message (string) |
||||
* [1] => level (string) |
||||
* [2] => category (string) |
||||
* [3] => timestamp (float, obtained by microtime(true)); |
||||
*/ |
||||
abstract protected function processLogs($messages); |
||||
} |
@ -0,0 +1,67 @@
|
||||
<?php |
||||
/** |
||||
* CWebLogRoute class file. |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @link http://www.yiiframework.com/ |
||||
* @copyright Copyright © 2008-2011 Yii Software LLC |
||||
* @license http://www.yiiframework.com/license/ |
||||
*/ |
||||
|
||||
/** |
||||
* CWebLogRoute shows the log content in Web page. |
||||
* |
||||
* The log content can appear either at the end of the current Web page |
||||
* or in FireBug console window (if {@link showInFireBug} is set true). |
||||
* |
||||
* @author Qiang Xue <qiang.xue@gmail.com> |
||||
* @version $Id: CWebLogRoute.php 3001 2011-02-24 16:42:44Z alexander.makarow $ |
||||
* @package system.logging |
||||
* @since 1.0 |
||||
*/ |
||||
class CWebLogRoute extends CLogRoute |
||||
{ |
||||
/** |
||||
* @var boolean whether the log should be displayed in FireBug instead of browser window. Defaults to false. |
||||
*/ |
||||
public $showInFireBug = false; |
||||
|
||||
/** |
||||
* @var boolean whether the log should be ignored in FireBug for ajax calls. Defaults to true. |
||||
* This option should be used carefully, because an ajax call returns all output as a result data. |
||||
* For example if the ajax call expects a json type result any output from the logger will cause ajax call to fail. |
||||
*/ |
||||
public $ignoreAjaxInFireBug = true; |
||||
|
||||
/** |
||||
* Displays the log messages. |
||||
* @param array $logs list of log messages |
||||
*/ |
||||
public function processLogs($logs) |
||||
{ |
||||
$this->render('log', $logs); |
||||
} |
||||
|
||||
/** |
||||
* Renders the view. |
||||
* @param string $view the view name (file name without extension). The file is assumed to be located under framework/data/views. |
||||
* @param array $data data to be passed to the view |
||||
*/ |
||||
protected function render($view, $data) |
||||
{ |
||||
$app = Yii::app(); |
||||
$isAjax = $app->getRequest()->getIsAjaxRequest(); |
||||
|
||||
if ($this->showInFireBug) |
||||
{ |
||||
if ($isAjax && $this->ignoreAjaxInFireBug) |
||||
return; |
||||
$view .= '-firebug'; |
||||
} |
||||
elseif (!($app instanceof CWebApplication) || $isAjax) |
||||
return; |
||||
|
||||
$viewFile = YII_PATH . DIRECTORY_SEPARATOR . 'views' . DIRECTORY_SEPARATOR . $view . '.php'; |
||||
include($app->findLocalizedFile($viewFile, 'en')); |
||||
} |
||||
} |
Loading…
Reference in new issue