Added new (clean) yii boilerplate
This commit is contained in:
39
framework/logging/CChainedLogFilter.php
Normal file
39
framework/logging/CChainedLogFilter.php
Normal file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
/**
|
||||
* CChainedLogFilter class file
|
||||
*
|
||||
* @author Carsten Brandt <mail@cebe.cc>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* CChainedLogFilter allows you to attach multiple log filters to a log route (See {@link CLogRoute::$filter} for details).
|
||||
*
|
||||
* @author Carsten Brandt <mail@cebe.cc>
|
||||
* @package system.logging
|
||||
* @since 1.1.13
|
||||
*/
|
||||
class CChainedLogFilter extends CComponent implements ILogFilter
|
||||
{
|
||||
/**
|
||||
* @var array list of filters to apply to the logs.
|
||||
* The value of each array element 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 classes should implement {@link ILogFilter} interface.
|
||||
* Filters will be applied in the order they are defined.
|
||||
*/
|
||||
public $filters=array();
|
||||
|
||||
/**
|
||||
* Filters the given log messages by applying all filters configured by {@link filters}.
|
||||
* @param array $logs the log messages
|
||||
*/
|
||||
public function filter(&$logs)
|
||||
{
|
||||
foreach($this->filters as $filter)
|
||||
Yii::createComponent($filter)->filter($logs);
|
||||
}
|
||||
}
|
||||
139
framework/logging/CDbLogRoute.php
Normal file
139
framework/logging/CDbLogRoute.php
Normal file
@@ -0,0 +1,139 @@
|
||||
<?php
|
||||
/**
|
||||
* CDbLogRoute class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 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>
|
||||
* @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();
|
||||
try
|
||||
{
|
||||
$db->createCommand()->delete($this->logTableName,'0=1');
|
||||
}
|
||||
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)
|
||||
{
|
||||
$db->createCommand()->createTable($tableName, array(
|
||||
'id'=>'pk',
|
||||
'level'=>'varchar(128)',
|
||||
'category'=>'varchar(128)',
|
||||
'logtime'=>'integer',
|
||||
'message'=>'text',
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* @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)
|
||||
{
|
||||
$command=$this->getDbConnection()->createCommand();
|
||||
foreach($logs as $log)
|
||||
{
|
||||
$command->insert($this->logTableName,array(
|
||||
'level'=>$log[1],
|
||||
'category'=>$log[2],
|
||||
'logtime'=>(int)$log[3],
|
||||
'message'=>$log[0],
|
||||
));
|
||||
}
|
||||
}
|
||||
}
|
||||
174
framework/logging/CEmailLogRoute.php
Normal file
174
framework/logging/CEmailLogRoute.php
Normal file
@@ -0,0 +1,174 @@
|
||||
<?php
|
||||
/**
|
||||
* CEmailLogRoute class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 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}.
|
||||
*
|
||||
* @property array $emails List of destination email addresses.
|
||||
* @property string $subject Email subject. Defaults to CEmailLogRoute::DEFAULT_SUBJECT.
|
||||
* @property string $sentFrom Send from address of the email.
|
||||
* @property array $headers Additional headers to use when sending an email.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @package system.logging
|
||||
* @since 1.0
|
||||
*/
|
||||
class CEmailLogRoute extends CLogRoute
|
||||
{
|
||||
/**
|
||||
* @var boolean set this property to true value in case log data you're going to send through emails contains
|
||||
* non-latin or UTF-8 characters. Emails would be UTF-8 encoded.
|
||||
* @since 1.1.13
|
||||
*/
|
||||
public $utf8=false;
|
||||
/**
|
||||
* @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($this->utf8)
|
||||
{
|
||||
$headers[]="MIME-Version: 1.0";
|
||||
$headers[]="Content-Type: text/plain; charset=UTF-8";
|
||||
$subject='=?UTF-8?B?'.base64_encode($subject).'?=';
|
||||
}
|
||||
if(($from=$this->getSentFrom())!==null)
|
||||
{
|
||||
$matches=array();
|
||||
preg_match_all('/([^<]*)<([^>]*)>/iu',$from,$matches);
|
||||
if(isset($matches[1][0],$matches[2][0]))
|
||||
{
|
||||
$name=$this->utf8 ? '=?UTF-8?B?'.base64_encode(trim($matches[1][0])).'?=' : trim($matches[1][0]);
|
||||
$from=trim($matches[2][0]);
|
||||
$headers[]="From: {$name} <{$from}>";
|
||||
}
|
||||
else
|
||||
$headers[]="From: {$from}";
|
||||
$headers[]="Reply-To: {$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);
|
||||
}
|
||||
}
|
||||
203
framework/logging/CFileLogRoute.php
Normal file
203
framework/logging/CFileLogRoute.php
Normal file
@@ -0,0 +1,203 @@
|
||||
<?php
|
||||
/**
|
||||
* CFileLogRoute class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 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.
|
||||
* If the property {@link rotateByCopy} is true, the primary log file will be
|
||||
* rotated by a copy and truncated (to be more compatible with log tailers)
|
||||
* otherwise it will be rotated by being renamed.
|
||||
*
|
||||
* @property string $logPath Directory storing log files. Defaults to application runtime path.
|
||||
* @property string $logFile Log file name. Defaults to 'application.log'.
|
||||
* @property integer $maxFileSize Maximum log file size in kilo-bytes (KB). Defaults to 1024 (1MB).
|
||||
* @property integer $maxLogFiles Number of files used for rotation. Defaults to 5.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @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';
|
||||
/**
|
||||
* @var boolean Whether to rotate primary log by copy and truncate
|
||||
* which is more compatible with log tailers. Defaults to false.
|
||||
* @since 1.1.14
|
||||
*/
|
||||
public $rotateByCopy=false;
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$text='';
|
||||
foreach($logs as $log)
|
||||
$text.=$this->formatLogMessage($log[0],$log[1],$log[2],$log[3]);
|
||||
|
||||
$logFile=$this->getLogPath().DIRECTORY_SEPARATOR.$this->getLogFile();
|
||||
$fp=@fopen($logFile,'a');
|
||||
@flock($fp,LOCK_EX);
|
||||
if(@filesize($logFile)>$this->getMaxFileSize()*1024)
|
||||
{
|
||||
$this->rotateFiles();
|
||||
@flock($fp,LOCK_UN);
|
||||
@fclose($fp);
|
||||
@file_put_contents($logFile,$text,FILE_APPEND|LOCK_EX);
|
||||
}
|
||||
else
|
||||
{
|
||||
@fwrite($fp,$text);
|
||||
@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))
|
||||
{
|
||||
// suppress errors because it's possible multiple processes enter into this section
|
||||
if($this->rotateByCopy)
|
||||
{
|
||||
@copy($file,$file.'.1');
|
||||
if($fp=@fopen($file,'a'))
|
||||
{
|
||||
@ftruncate($fp,0);
|
||||
@fclose($fp);
|
||||
}
|
||||
}
|
||||
else
|
||||
@rename($file,$file.'.1');
|
||||
}
|
||||
}
|
||||
}
|
||||
140
framework/logging/CLogFilter.php
Normal file
140
framework/logging/CLogFilter.php
Normal file
@@ -0,0 +1,140 @@
|
||||
<?php
|
||||
/**
|
||||
* CLogFilter class file
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 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>
|
||||
* @package system.logging
|
||||
*/
|
||||
class CLogFilter extends CComponent implements ILogFilter
|
||||
{
|
||||
/**
|
||||
* @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');
|
||||
/**
|
||||
* @var callable or function which will be used to dump context information.
|
||||
* Defaults to `var_export`. If you're experiencing issues with circular references
|
||||
* problem change it to `print_r`. Any kind of callable (static methods, user defined
|
||||
* functions, lambdas, etc.) could also be used.
|
||||
* @since 1.1.14
|
||||
*/
|
||||
public $dumper='var_export';
|
||||
|
||||
|
||||
/**
|
||||
* 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().')';
|
||||
|
||||
if($this->dumper==='var_export' || $this->dumper==='print_r')
|
||||
{
|
||||
foreach($this->logVars as $name)
|
||||
if(($value=$this->getGlobalsValue($name))!==null)
|
||||
$context[]="\${$name}=".call_user_func($this->dumper,$value,true);
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach($this->logVars as $name)
|
||||
if(($value=$this->getGlobalsValue($name))!==null)
|
||||
$context[]="\${$name}=".call_user_func($this->dumper,$value);
|
||||
}
|
||||
|
||||
return implode("\n\n",$context);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $path
|
||||
* @return string|null
|
||||
*/
|
||||
private function getGlobalsValue(&$path)
|
||||
{
|
||||
if(is_scalar($path))
|
||||
return !empty($GLOBALS[$path]) ? $GLOBALS[$path] : null;
|
||||
$pathAux=$path;
|
||||
$parts=array();
|
||||
$value=$GLOBALS;
|
||||
do
|
||||
{
|
||||
$value=$value[$parts[]=array_shift($pathAux)];
|
||||
}
|
||||
while(!empty($value) && !empty($pathAux) && !is_string($value));
|
||||
$path=implode('.',$parts);
|
||||
return $value;
|
||||
}
|
||||
}
|
||||
121
framework/logging/CLogRoute.php
Normal file
121
framework/logging/CLogRoute.php
Normal file
@@ -0,0 +1,121 @@
|
||||
<?php
|
||||
/**
|
||||
* CLogRoute class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* CLogRoute is the base class for all log route classes.
|
||||
*
|
||||
* A log route 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>
|
||||
* @package system.logging
|
||||
* @since 1.0
|
||||
*/
|
||||
abstract class CLogRoute extends CComponent
|
||||
{
|
||||
/**
|
||||
* @var boolean whether to enable this log route. 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 mixed array of categories, or string list separated by comma or space.
|
||||
* Defaults to empty array, meaning all categories.
|
||||
*/
|
||||
public $categories=array();
|
||||
/**
|
||||
* @var mixed array of categories, or string list separated by comma or space, to EXCLUDE from logs.
|
||||
* Defaults to empty array, meaning no categories are excluded.
|
||||
* This will exclude any categories after $categories has been ran.
|
||||
*/
|
||||
public $except=array();
|
||||
/**
|
||||
* @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 implement {@link ILogFilter} interface.
|
||||
* If you want to apply multiple filters you can use {@link CChainedLogFilter} to do so.
|
||||
* Defaults to null, meaning no filter will be used.
|
||||
*/
|
||||
public $filter;
|
||||
/**
|
||||
* @var array the logs that are collected so far by this log route.
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public $logs=array();
|
||||
|
||||
|
||||
/**
|
||||
* Initializes the route.
|
||||
* This method is invoked after the route is created by the route manager.
|
||||
*/
|
||||
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 formatLogMessage($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 logs after they are collected from the logger
|
||||
*/
|
||||
public function collectLogs($logger, $processLogs=false)
|
||||
{
|
||||
$logs=$logger->getLogs($this->levels,$this->categories,$this->except);
|
||||
$this->logs=empty($this->logs) ? $logs : array_merge($this->logs,$logs);
|
||||
if($processLogs && !empty($this->logs))
|
||||
{
|
||||
if($this->filter!==null)
|
||||
Yii::createComponent($this->filter)->filter($this->logs);
|
||||
if($this->logs!==array())
|
||||
$this->processLogs($this->logs);
|
||||
$this->logs=array();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Processes log messages and sends them to specific destination.
|
||||
* Derived child classes must implement this method.
|
||||
* @param array $logs list of messages. Each array element 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($logs);
|
||||
}
|
||||
126
framework/logging/CLogRouter.php
Normal file
126
framework/logging/CLogRouter.php
Normal file
@@ -0,0 +1,126 @@
|
||||
<?php
|
||||
/**
|
||||
* CLogRouter class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* CLogRouter manages log routes that record log messages in different media.
|
||||
*
|
||||
* For example, a file log route {@link CFileLogRoute} records log messages
|
||||
* in log files. An email log route {@link CEmailLogRoute} sends log messages
|
||||
* to specific email addresses. See {@link CLogRoute} for more details about
|
||||
* different log routes.
|
||||
*
|
||||
* Log routes may be configured in application configuration like following:
|
||||
* <pre>
|
||||
* array(
|
||||
* 'preload'=>array('log'), // preload log component when app starts
|
||||
* 'components'=>array(
|
||||
* 'log'=>array(
|
||||
* 'class'=>'CLogRouter',
|
||||
* 'routes'=>array(
|
||||
* array(
|
||||
* 'class'=>'CFileLogRoute',
|
||||
* 'levels'=>'trace, info',
|
||||
* 'categories'=>'system.*',
|
||||
* ),
|
||||
* array(
|
||||
* 'class'=>'CEmailLogRoute',
|
||||
* 'levels'=>'error, warning',
|
||||
* 'emails'=>array('admin@example.com'),
|
||||
* ),
|
||||
* ),
|
||||
* ),
|
||||
* ),
|
||||
* )
|
||||
* </pre>
|
||||
*
|
||||
* You can specify multiple routes with different filtering conditions and different
|
||||
* targets, even if the routes are of the same type.
|
||||
*
|
||||
* @property array $routes The currently initialized routes.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @package system.logging
|
||||
* @since 1.0
|
||||
*/
|
||||
class CLogRouter extends CApplicationComponent
|
||||
{
|
||||
private $_routes=array();
|
||||
|
||||
/**
|
||||
* Initializes this application component.
|
||||
* This method is required by the IApplicationComponent interface.
|
||||
*/
|
||||
public function init()
|
||||
{
|
||||
parent::init();
|
||||
foreach($this->_routes as $name=>$route)
|
||||
{
|
||||
$route=Yii::createComponent($route);
|
||||
$route->init();
|
||||
$this->_routes[$name]=$route;
|
||||
}
|
||||
Yii::getLogger()->attachEventHandler('onFlush',array($this,'collectLogs'));
|
||||
Yii::app()->attachEventHandler('onEndRequest',array($this,'processLogs'));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array the currently initialized routes
|
||||
*/
|
||||
public function getRoutes()
|
||||
{
|
||||
return new CMap($this->_routes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array $config list of route configurations. Each array element represents
|
||||
* the configuration for a single route and has the following array structure:
|
||||
* <ul>
|
||||
* <li>class: specifies the class name or alias for the route class.</li>
|
||||
* <li>name-value pairs: configure the initial property values of the route.</li>
|
||||
* </ul>
|
||||
*/
|
||||
public function setRoutes($config)
|
||||
{
|
||||
foreach($config as $name=>$route)
|
||||
$this->_routes[$name]=$route;
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects log messages from a logger.
|
||||
* This method is an event handler to the {@link CLogger::onFlush} event.
|
||||
* @param CEvent $event event parameter
|
||||
*/
|
||||
public function collectLogs($event)
|
||||
{
|
||||
$logger=Yii::getLogger();
|
||||
$dumpLogs=isset($event->params['dumpLogs']) && $event->params['dumpLogs'];
|
||||
foreach($this->_routes as $route)
|
||||
{
|
||||
if($route->enabled)
|
||||
$route->collectLogs($logger,$dumpLogs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Collects and processes log messages from a logger.
|
||||
* This method is an event handler to the {@link CApplication::onEndRequest} event.
|
||||
* @param CEvent $event event parameter
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public function processLogs($event)
|
||||
{
|
||||
$logger=Yii::getLogger();
|
||||
foreach($this->_routes as $route)
|
||||
{
|
||||
if($route->enabled)
|
||||
$route->collectLogs($logger,true);
|
||||
}
|
||||
}
|
||||
}
|
||||
354
framework/logging/CLogger.php
Normal file
354
framework/logging/CLogger.php
Normal file
@@ -0,0 +1,354 @@
|
||||
<?php
|
||||
/**
|
||||
* CLogger class file
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 Yii Software LLC
|
||||
* @license http://www.yiiframework.com/license/
|
||||
*/
|
||||
|
||||
/**
|
||||
* CLogger records log messages in memory.
|
||||
*
|
||||
* CLogger implements the methods to retrieve the messages with
|
||||
* various filter conditions, including log levels and log categories.
|
||||
*
|
||||
* @property array $logs List of messages. Each array element represents one message
|
||||
* with the following structure:
|
||||
* array(
|
||||
* [0] => message (string)
|
||||
* [1] => level (string)
|
||||
* [2] => category (string)
|
||||
* [3] => timestamp (float, obtained by microtime(true));.
|
||||
* @property float $executionTime The total time for serving the current request.
|
||||
* @property integer $memoryUsage Memory usage of the application (in bytes).
|
||||
* @property array $profilingResults The profiling results.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @package system.logging
|
||||
* @since 1.0
|
||||
*/
|
||||
class CLogger extends CComponent
|
||||
{
|
||||
const LEVEL_TRACE='trace';
|
||||
const LEVEL_WARNING='warning';
|
||||
const LEVEL_ERROR='error';
|
||||
const LEVEL_INFO='info';
|
||||
const LEVEL_PROFILE='profile';
|
||||
|
||||
/**
|
||||
* @var integer how many messages should be logged before they are flushed to destinations.
|
||||
* Defaults to 10,000, meaning for every 10,000 messages, the {@link flush} method will be
|
||||
* automatically invoked once. If this is 0, it means messages will never be flushed automatically.
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public $autoFlush=10000;
|
||||
/**
|
||||
* @var boolean this property will be passed as the parameter to {@link flush()} when it is
|
||||
* called in {@link log()} due to the limit of {@link autoFlush} being reached.
|
||||
* By default, this property is false, meaning the filtered messages are still kept in the memory
|
||||
* by each log route after calling {@link flush()}. If this is true, the filtered messages
|
||||
* will be written to the actual medium each time {@link flush()} is called within {@link log()}.
|
||||
* @since 1.1.8
|
||||
*/
|
||||
public $autoDump=false;
|
||||
/**
|
||||
* @var array log messages
|
||||
*/
|
||||
private $_logs=array();
|
||||
/**
|
||||
* @var integer number of log messages
|
||||
*/
|
||||
private $_logCount=0;
|
||||
/**
|
||||
* @var array log levels for filtering (used when filtering)
|
||||
*/
|
||||
private $_levels;
|
||||
/**
|
||||
* @var array log categories for filtering (used when filtering)
|
||||
*/
|
||||
private $_categories;
|
||||
/**
|
||||
* @var array log categories for excluding from filtering (used when filtering)
|
||||
*/
|
||||
private $_except=array();
|
||||
/**
|
||||
* @var array the profiling results (category, token => time in seconds)
|
||||
*/
|
||||
private $_timings;
|
||||
/**
|
||||
* @var boolean if we are processing the log or still accepting new log messages
|
||||
* @since 1.1.9
|
||||
*/
|
||||
private $_processing=false;
|
||||
|
||||
/**
|
||||
* Logs a message.
|
||||
* Messages logged by this method may be retrieved back via {@link getLogs}.
|
||||
* @param string $message message to be logged
|
||||
* @param string $level level of the message (e.g. 'Trace', 'Warning', 'Error'). It is case-insensitive.
|
||||
* @param string $category category of the message (e.g. 'system.web'). It is case-insensitive.
|
||||
* @see getLogs
|
||||
*/
|
||||
public function log($message,$level='info',$category='application')
|
||||
{
|
||||
$this->_logs[]=array($message,$level,$category,microtime(true));
|
||||
$this->_logCount++;
|
||||
if($this->autoFlush>0 && $this->_logCount>=$this->autoFlush && !$this->_processing)
|
||||
{
|
||||
$this->_processing=true;
|
||||
$this->flush($this->autoDump);
|
||||
$this->_processing=false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 array|string $categories category filter
|
||||
* @param array|string $except list of log categories to ignore
|
||||
* @return array list of messages. Each array element 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=array(), $except=array())
|
||||
{
|
||||
$this->_levels=preg_split('/[\s,]+/',strtolower($levels),-1,PREG_SPLIT_NO_EMPTY);
|
||||
|
||||
if (is_string($categories))
|
||||
$this->_categories=preg_split('/[\s,]+/',strtolower($categories),-1,PREG_SPLIT_NO_EMPTY);
|
||||
else
|
||||
$this->_categories=array_filter(array_map('strtolower',$categories));
|
||||
|
||||
if (is_string($except))
|
||||
$this->_except=preg_split('/[\s,]+/',strtolower($except),-1,PREG_SPLIT_NO_EMPTY);
|
||||
else
|
||||
$this->_except=array_filter(array_map('strtolower',$except));
|
||||
|
||||
$ret=$this->_logs;
|
||||
|
||||
if(!empty($levels))
|
||||
$ret=array_values(array_filter($ret,array($this,'filterByLevel')));
|
||||
|
||||
if(!empty($this->_categories) || !empty($this->_except))
|
||||
$ret=array_values(array_filter($ret,array($this,'filterByCategory')));
|
||||
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter function used by {@link getLogs}
|
||||
* @param array $value element to be filtered
|
||||
* @return boolean true if valid log, false if not.
|
||||
*/
|
||||
private function filterByCategory($value)
|
||||
{
|
||||
return $this->filterAllCategories($value, 2);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter function used by {@link getProfilingResults}
|
||||
* @param array $value element to be filtered
|
||||
* @return boolean true if valid timing entry, false if not.
|
||||
*/
|
||||
private function filterTimingByCategory($value)
|
||||
{
|
||||
return $this->filterAllCategories($value, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter function used to filter included and excluded categories
|
||||
* @param array $value element to be filtered
|
||||
* @param integer $index index of the values array to be used for check
|
||||
* @return boolean true if valid timing entry, false if not.
|
||||
*/
|
||||
private function filterAllCategories($value, $index)
|
||||
{
|
||||
$cat=strtolower($value[$index]);
|
||||
$ret=empty($this->_categories);
|
||||
foreach($this->_categories as $category)
|
||||
{
|
||||
if($cat===$category || (($c=rtrim($category,'.*'))!==$category && strpos($cat,$c)===0))
|
||||
$ret=true;
|
||||
}
|
||||
if($ret)
|
||||
{
|
||||
foreach($this->_except as $category)
|
||||
{
|
||||
if($cat===$category || (($c=rtrim($category,'.*'))!==$category && strpos($cat,$c)===0))
|
||||
$ret=false;
|
||||
}
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Filter function used by {@link getLogs}
|
||||
* @param array $value element to be filtered
|
||||
* @return boolean true if valid log, false if not.
|
||||
*/
|
||||
private function filterByLevel($value)
|
||||
{
|
||||
return in_array(strtolower($value[1]),$this->_levels);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the total time for serving the current request.
|
||||
* This method calculates the difference between now and the timestamp
|
||||
* defined by constant YII_BEGIN_TIME.
|
||||
* To estimate the execution time more accurately, the constant should
|
||||
* be defined as early as possible (best at the beginning of the entry script.)
|
||||
* @return float the total time for serving the current request.
|
||||
*/
|
||||
public function getExecutionTime()
|
||||
{
|
||||
return microtime(true)-YII_BEGIN_TIME;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the memory usage of the current application.
|
||||
* This method relies on the PHP function memory_get_usage().
|
||||
* If it is not available, the method will attempt to use OS programs
|
||||
* to determine the memory usage. A value 0 will be returned if the
|
||||
* memory usage can still not be determined.
|
||||
* @return integer memory usage of the application (in bytes).
|
||||
*/
|
||||
public function getMemoryUsage()
|
||||
{
|
||||
if(function_exists('memory_get_usage'))
|
||||
return memory_get_usage();
|
||||
else
|
||||
{
|
||||
$output=array();
|
||||
if(strncmp(PHP_OS,'WIN',3)===0)
|
||||
{
|
||||
exec('tasklist /FI "PID eq ' . getmypid() . '" /FO LIST',$output);
|
||||
return isset($output[5])?preg_replace('/[\D]/','',$output[5])*1024 : 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
$pid=getmypid();
|
||||
exec("ps -eo%mem,rss,pid | grep $pid", $output);
|
||||
$output=explode(" ",$output[0]);
|
||||
return isset($output[1]) ? $output[1]*1024 : 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* Since 1.1.11, filtering results by category supports the same format used for filtering logs in
|
||||
* {@link getLogs}, and similarly supports filtering by multiple categories and wildcard.
|
||||
* @param string $token token filter. Defaults to null, meaning not filtered by token.
|
||||
* @param string $categories 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,$categories=null,$refresh=false)
|
||||
{
|
||||
if($this->_timings===null || $refresh)
|
||||
$this->calculateTimings();
|
||||
if($token===null && $categories===null)
|
||||
return $this->_timings;
|
||||
|
||||
$timings = $this->_timings;
|
||||
if($categories!==null) {
|
||||
$this->_categories=preg_split('/[\s,]+/',strtolower($categories),-1,PREG_SPLIT_NO_EMPTY);
|
||||
$timings=array_filter($timings,array($this,'filterTimingByCategory'));
|
||||
}
|
||||
|
||||
$results=array();
|
||||
foreach($timings as $timing)
|
||||
{
|
||||
if($token===null || $timing[0]===$token)
|
||||
$results[]=$timing[2];
|
||||
}
|
||||
return $results;
|
||||
}
|
||||
|
||||
private function calculateTimings()
|
||||
{
|
||||
$this->_timings=array();
|
||||
|
||||
$stack=array();
|
||||
foreach($this->_logs as $log)
|
||||
{
|
||||
if($log[1]!==CLogger::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 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];
|
||||
$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 $dumpLogs whether to process the logs immediately as they are passed to log route
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public function flush($dumpLogs=false)
|
||||
{
|
||||
$this->onFlush(new CEvent($this, array('dumpLogs'=>$dumpLogs)));
|
||||
$this->_logs=array();
|
||||
$this->_logCount=0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Raises an <code>onFlush</code> event.
|
||||
* @param CEvent $event the event parameter
|
||||
* @since 1.1.0
|
||||
*/
|
||||
public function onFlush($event)
|
||||
{
|
||||
$this->raiseEvent('onFlush', $event);
|
||||
}
|
||||
}
|
||||
205
framework/logging/CProfileLogRoute.php
Normal file
205
framework/logging/CProfileLogRoute.php
Normal file
@@ -0,0 +1,205 @@
|
||||
<?php
|
||||
/**
|
||||
* CProfileLogRoute class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 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>
|
||||
* @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'.
|
||||
* @throws CException if given value is not "summary" or "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
|
||||
* @throws CException if Yii::beginProfile() and Yii::endProfile() are not matching
|
||||
*/
|
||||
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
|
||||
* @throws CException if Yii::beginProfile() and Yii::endProfile() are not matching
|
||||
*/
|
||||
protected function displaySummary($logs)
|
||||
{
|
||||
$stack=array();
|
||||
$results=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);
|
||||
}
|
||||
}
|
||||
85
framework/logging/CWebLogRoute.php
Normal file
85
framework/logging/CWebLogRoute.php
Normal file
@@ -0,0 +1,85 @@
|
||||
<?php
|
||||
/**
|
||||
* CWebLogRoute class file.
|
||||
*
|
||||
* @author Qiang Xue <qiang.xue@gmail.com>
|
||||
* @link http://www.yiiframework.com/
|
||||
* @copyright 2008-2013 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>
|
||||
* @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;
|
||||
/**
|
||||
* @var boolean whether the log should be ignored in FireBug for Flash/Flex calls. Defaults to true.
|
||||
* This option should be used carefully, because an Flash/Flex call returns all output as a result data.
|
||||
* For example if the Flash/Flex call expects an XML type result any output from the logger will cause Flash/Flex call to fail.
|
||||
* @since 1.1.11
|
||||
*/
|
||||
public $ignoreFlashInFireBug=true;
|
||||
/**
|
||||
* @var boolean whether the log should be collapsed by default in Firebug. Defaults to false.
|
||||
* @since 1.1.13.
|
||||
*/
|
||||
public $collapsedInFireBug=false;
|
||||
|
||||
/**
|
||||
* 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();
|
||||
$isFlash=$app->getRequest()->getIsFlashRequest();
|
||||
|
||||
if($this->showInFireBug)
|
||||
{
|
||||
// do not output anything for ajax and/or flash requests if needed
|
||||
if($isAjax && $this->ignoreAjaxInFireBug || $isFlash && $this->ignoreFlashInFireBug)
|
||||
return;
|
||||
$view.='-firebug';
|
||||
if(($userAgent=$app->getRequest()->getUserAgent())!==null && preg_match('/msie [5-9]/i',$userAgent))
|
||||
{
|
||||
echo '<script type="text/javascript">';
|
||||
echo file_get_contents(dirname(__FILE__).'/../vendors/console-normalizer/normalizeconsole.min.js');
|
||||
echo "</script>\n";
|
||||
}
|
||||
}
|
||||
elseif(!($app instanceof CWebApplication) || $isAjax || $isFlash)
|
||||
return;
|
||||
|
||||
$viewFile=YII_PATH.DIRECTORY_SEPARATOR.'views'.DIRECTORY_SEPARATOR.$view.'.php';
|
||||
include($app->findLocalizedFile($viewFile,'en'));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user