Browse Source

w

tags/2.0.0-beta
Qiang Xue 13 years ago
parent
commit
6d2e0ba63d
  1. 7
      framework/YiiBase.php
  2. 5
      framework/base/Component.php
  3. 19
      framework/base/Initable.php
  4. 12
      framework/base/Model.php
  5. 142
      framework/logging/EmailTarget.php
  6. 107
      framework/logging/Filter.php
  7. 190
      framework/logging/Logger.php
  8. 5
      framework/logging/Router.php
  9. 239
      framework/logging/Target.php

7
framework/YiiBase.php

@ -160,6 +160,9 @@ class YiiBase
* In the latter case, the root alias will be replaced by the corresponding registered path
* and the remaining part will be appended to it.
*
* In case the given alias is not an alias (i.e., not starting with '@'),
* it will be returned back as is.
*
* Note, this method does not ensure the existence of the resulting path.
* @param string $alias alias
* @return mixed path corresponding to the alias, false if the root alias is not previously registered.
@ -175,6 +178,9 @@ class YiiBase
if (isset(self::$aliases[$rootAlias])) {
return self::$aliases[$alias] = self::$aliases[$rootAlias] . substr($alias, $pos);
}
else if($alias[0] !== '@') { // not an alias
return $alias;
}
}
return false;
}
@ -344,7 +350,6 @@ class YiiBase
}
if ($object instanceof Initable) {
$object->preinit();
foreach ($config as $name => $value) {
$object->$name = $value;
}

5
framework/base/Component.php

@ -307,9 +307,9 @@ class Component
* Creates a new component instance.
*
* This method differs from the PHP `new` operator in that it does the following
* additional work after the component is created:
* steps to create a new component instance:
*
* - Call [[Initable::preinit|preinit]] if the class implements [[Initable]];
* - Call class constructor (same the `new` operator);
* - Initialize the component properties using the name-value pairs given as the
* last parameter to this method;
* - Call [[Initable::init|init]] if the class implements [[Initable]].
@ -329,7 +329,6 @@ class Component
* $model = Foo::create(1, 2, array('c' => 3));
* // which is equivalent to the following lines:
* $model = new Foo(1, 2);
* $model->preinit();
* $model->c = 3;
* $model->init();
* ~~~

19
framework/base/Initable.php

@ -13,9 +13,9 @@ namespace yii\base;
* Initable is an interface indicating a class needs initialization to work properly.
*
* Initable requires a class to implement the [[init]] method.
* When [[\Yii::createComponent]] is creating a new component instance, if the component
* class implements Initable interface, the method will call its [[init]] method
* after setting the initial values of the component properties.
* When [[\Yii::createComponent]] is being used to create a new component which implements
* Initable, it will call the [[init]] method after setting the initial values of the
* component properties.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
@ -23,23 +23,10 @@ namespace yii\base;
interface Initable
{
/**
* Pre-initializes this component.
* This method is invoked by [[\Yii::createComponent]] after its creates the new
* component instance, but BEFORE the component properties are initialized.
*
* You may implement this method to do work such as setting property default values.
*/
public function preinit();
/**
* Initializes this component.
* This method is invoked by [[\Yii::createComponent]] after its creates the new
* component instance and initializes the component properties. In other words,
* at this stage, the component has been fully configured.
*
* The default implementation calls [[behaviors]] and registers any available behaviors.
* You may override this method with additional initialization logic (e.g. establish DB connection).
* Make sure you call the parent implementation.
*/
public function init();
}

12
framework/base/Model.php

@ -50,18 +50,6 @@ class Model extends Component implements Initable, \IteratorAggregate, \ArrayAcc
}
/**
* Pre-initializes this model.
* This method is required by the [[Initable]] interface. It is invoked by
* [[\Yii::createComponent]] after its creates the new model instance but
* BEFORE the model properties are initialized.
*
* You may override this method to do work such as setting property default values.
*/
public function preinit()
{
}
/**
* Initializes this model.
*
* This method is required by the [[Initable]] interface. It is invoked by [[\Yii::createComponent]]

142
framework/logging/EmailTarget.php

@ -1,146 +1,76 @@
<?php
/**
* CEmailLogRoute class file.
* EmailTarget class file.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008-2011 Yii Software LLC
* @copyright Copyright &copy; 2008-2012 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
namespace yii\logging;
/**
* CEmailLogRoute sends selected log messages to email addresses.
* EmailTarget sends selected log messages to the specified 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}.
* The target email addresses may be specified via [[emails]] property.
* Optionally, you may set the email [[subject]], [[sentFrom]] address and
* additional [[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
* @since 2.0
*/
class CEmailLogRoute extends CLogRoute
class EmailTarget extends Target
{
/**
* @var array list of destination email addresses.
*/
private $_email = array();
public $emails = array();
/**
* @var string email subject
*/
private $_subject;
public $subject;
/**
* @var string email sent from address
* @var string email sent-from address
*/
private $_from;
public $sentFrom;
/**
* @var array list of additional headers to use when sending an email.
*/
private $_headers = array();
public $headers = array();
/**
* Sends log messages to specified email addresses.
* @param array $logs list of log messages
* Sends log [[messages]] to specified email addresses.
* @param boolean $final whether this method is called at the end of the current application
*/
protected function processLogs($logs)
public function exportMessages($final)
{
$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);
$body = '';
foreach ($this->messages as $message) {
$body .= $this->formatMessage($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));
$body = wordwrap($body, 70);
$subject = $this->subject === null ? Yii::t('yii', 'Application Log') : $this->subject;
foreach ($this->emails as $email) {
$this->sendEmail($subject, $body, $email, $this->sentFrom, $this->headers);
}
/**
* @return array list of destination email addresses
*/
public function getEmails()
{
return $this->_email;
$this->messages = array();
}
/**
* @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
* Sends an email.
* @param string $subject email subject
* @param string $body email body
* @param string $sentTo sent-to email address
* @param string $sentFrom sent-from email address
* @param array $headers additional headers to be used when sending the email
*/
public function getHeaders()
protected function sendEmail($subject, $body, $sentTo, $sentFrom, $headers)
{
return $this->_headers;
if ($sentFrom !== null) {
$headers[] = "From: {$sentFrom}";
}
/**
* @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);
mail($email, $subject, $body, implode("\r\n", $headers));
}
}

107
framework/logging/Filter.php

@ -1,107 +0,0 @@
<?php
/**
* CLogFilter class file
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @link http://www.yiiframework.com/
* @copyright Copyright &copy; 2008-2011 Yii Software LLC
* @license http://www.yiiframework.com/license/
*/
/**
* 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);
}
}

190
framework/logging/Logger.php

@ -38,20 +38,26 @@ class Logger extends \yii\base\Component
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.
* called due to the [[flushInterval]] is reached. Defaults to true, meaning the flushed
* messages will be exported to the actual storage medium (e.g. DB, email) defined by each
* log target. If false, the flushed messages will be kept in the memory of each log target.
* @see flushInterval
*/
public $autoExport = false;
public $autoExport = true;
/**
* @var array logged messages. This property is mainly managed by [[log]] and [[flush]].
* Each log message is of the following structure:
*
* ~~~
* array(
* [0] => message (string)
* [1] => level (string)
* [2] => category (string)
* [3] => timestamp (float, obtained by microtime(true))
* )
* ~~~
*/
public $messages = array();
/**
* @var array the profiling results (category, token => time in seconds)
*/
private $_timings;
/**
* Logs an error message.
@ -155,72 +161,24 @@ class Logger extends \yii\base\Component
}
/**
* 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.
* 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.
*/
private function filterByCategory($value)
{
foreach ($this->_categories as $category)
public function flush($export = false)
{
$cat = strtolower($value[2]);
if ($cat === $category || (($c = rtrim($category, '.*')) !== $category && strpos($cat, $c) === 0))
return $value;
}
return false;
$this->onFlush(new \yii\base\Event($this, array('export' => $export, 'flush' => true)));
$this->messages = array();
}
/**
* Filter function used by {@link getLogs}
* @param array $value element to be filtered
* @return array valid log, false if not.
* Raises an `onFlush` event.
* @param \yii\base\Event $event the event parameter
*/
private function filterByLevel($value)
public function onFlush($event)
{
return in_array(strtolower($value[1]), $this->_levels) ? $value : false;
$this->raiseEvent('onFlush', $event);
}
/**
@ -237,36 +195,58 @@ class Logger extends \yii\base\Component
/**
* 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.
*
* By default, all profiling results will be returned. You may provide
* `$categories` and `$excludeCategories` as parameters to retrieve the
* results that you are interested in.
*
* @param array $categories list of categories that you are interested in.
* You can use an asterisk at the end of a category to do a prefix match.
* For example, 'yii\db\*' will match categories starting with 'yii\db\',
* such as 'yii\db\dao\Connection'.
* @param array $excludeCategories list of categories that you are interested in.
* @return array the profiling results. Each array element has the following structure:
* `array($category, $time)`.
*/
public function getProfilingResults($token = null, $category = null, $refresh = false)
public function getProfiling($categories = array(), $excludeCategories = array())
{
if ($this->_timings === null || $refresh) {
$this->calculateTimings();
$timings = $this->calculateTimings();
if (empty($categories) && empty($excludeCategories)) {
return $timings;
}
foreach ($timings as $i => $timing) {
$matched = empty($categories);
foreach ($categories as $category) {
$prefix = rtrim($category, '*');
if (strpos($timing[0], $prefix) === 0 && ($timing[0] === $category || $prefix !== $category)) {
$matched = true;
break;
}
}
if ($matched) {
foreach ($excludeCategories as $category) {
$prefix = rtrim($category, '*');
foreach ($timings as $i => $timing) {
if (strpos($timing[0], $prefix) === 0 && ($timing[0] === $category || $prefix !== $category)) {
$matched = false;
break;
}
}
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];
}
if (!$matched) {
unset($timings[$i]);
}
}
return $results;
return array_values($timings);
}
private function calculateTimings()
{
$this->_timings = array();
$timings = array();
$stack = array();
foreach ($this->messages as $log) {
@ -274,18 +254,16 @@ class Logger extends \yii\base\Component
continue;
}
list($message, $level, $category, $timestamp) = $log;
if (!strncasecmp($message, 'begin:', 6)) {
$log[0] = substr($message, 6);
if ($message === 'begin') {
$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 { // $message === 'end'
if (($last = array_pop($stack)) !== null && $last[2] === $category) {
$delta = $timestamp - $last[3];
$timings[] = array($category, $delta);
}
else {
throw new \yii\base\Exception('Found a mismatching profiling block: ' . $token);
throw new \yii\base\Exception('Found a mismatching profiling block: ' . $category);
}
}
}
@ -293,28 +271,10 @@ class Logger extends \yii\base\Component
$now = microtime(true);
while (($last = array_pop($stack)) !== null) {
$delta = $now - $last[3];
$this->_timings[] = array($last[0], $last[2], $delta);
}
$timings[] = array($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();
return $timings;
}
/**
* Raises an `onFlush` event.
* @param \yii\base\Event $event the event parameter
*/
public function onFlush($event)
{
$this->raiseEvent('onFlush', $event);
}
}

5
framework/logging/Router.php

@ -125,11 +125,12 @@ class Router extends \yii\base\ApplicationComponent
*/
public function processMessages($event)
{
$logger = Yii::getLogger();
$messages = \Yii::getLogger()->messages;
$export = !isset($event->params['export']) || $event->params['export'];
$final = !isset($event-params['flush']) || !$event->params['flush'];
foreach ($this->_targets as $target) {
if ($target->enabled) {
$target->processMessages($logger, $export);
$target->processMessages($messages, $export, $final);
}
}
}

239
framework/logging/Target.php

@ -13,18 +13,15 @@ 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').
* A log target object will filter the messages logged by [[Logger]] according
* to its [[levels]] and [[categories]] properties. It may also export the filtered
* messages to specific destination defined by the target, such as emails, files.
*
* Level filter and category filter are combinational, i.e., only messages
* satisfying both filter conditions will they be returned.
* satisfying both filter conditions will they be returned. Additionally, you
* may specify [[excludeCategories]]. If a message's category falls within the excluded
* categories, it will be filtered out, even if it passes the [[levels]] and
* [[categories]] filters.
*
* @author Qiang Xue <qiang.xue@gmail.com>
* @since 2.0
@ -36,42 +33,57 @@ abstract class Target extends \yii\base\Component implements \yii\base\Initable
*/
public $enabled = true;
/**
* @var string list of levels separated by comma or space. Defaults to empty, meaning all levels.
* @var array list of message levels that this target is interested in. Defaults to empty, meaning all levels.
*/
public $levels = array();
/**
* @var array list of message categories that this target is interested in. Defaults to empty, meaning all categories.
* You can use an asterisk at the end of a category so that the category may be used to
* match those categories sharing the same common prefix. For example, 'yii\db\*' will match
* categories starting with 'yii\db\', such as 'yii\db\dao\Connection'.
*/
public $categories = array();
/**
* @var array list of message categories that this target is NOT interested in. Defaults to empty, meaning no uninteresting messages.
* If this property is not empty, then any category listed here will be excluded from [[categories]].
* You can use an asterisk at the end of a category so that the category can be used to
* match those categories sharing the same common prefix. For example, 'yii\db\*' will match
* categories starting with 'yii\db\', such as 'yii\db\dao\Connection'.
* @see categories
*/
public $levels;
public $excludeCategories = array();
/**
* @var string list of categories separated by comma or space. Defaults to empty, meaning all categories.
* @var boolean whether to prefix each log message with the current session ID. Defaults to false.
*/
public $categories;
public $prefixSession = false;
/**
* @var string list of categories that should be excluded.
* @var boolean whether to prefix each log message with the current user name and ID. Defaults to false.
* @see \yii\web\User
*/
public $excludeCategories;
public $prefixUser = false;
/**
* @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.
* @var boolean whether to log a message containing the current user name and ID. Defaults to true.
* @see \yii\web\User
*/
public $filter;
public $logUser = false;
/**
* @var array the messages that are collected so far by this log target.
* @var array list of the PHP predefined variables that should be logged in a message.
* Note that a variable must be accessible via `$GLOBALS`. Otherwise it won't be logged.
* Defaults to `array('_GET', '_POST', '_FILES', '_COOKIE', '_SESSION', '_SERVER')`.
*/
public $messages;
public $logVars = array('_GET', '_POST', '_FILES', '_COOKIE', '_SESSION', '_SERVER');
/**
* @var array the messages that are retrieved from the logger so far by this log target.
*/
public $messages = array();
/**
* 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.
* Exports log messages to a specific destination.
* Child classes must implement this method. Note that you may need
* to clean up [[messages]] in this method to avoid re-exporting messages.
* @param boolean $final whether this method is called at the end of the current application
*/
public function preinit()
{
}
abstract public function exportMessages($final);
/**
* Initializes this component.
@ -83,89 +95,126 @@ abstract class Target extends \yii\base\Component implements \yii\base\Initable
}
/**
* 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
* Processes the given log messages.
* This method will filter the given messages with [[levels]] and [[categories]].
* And if requested, it will also export the filtering result to specific medium (e.g. email).
* @param array $messages log messages to be processed. See [[Logger::messages]] for the structure
* of each message.
* @param boolean $export whether to export the processing result
* @param boolean $final whether this method is called at the end of the current application
*/
protected function formatMessage($message, $level, $category, $time)
public function processMessages($messages, $export, $final)
{
return @date('Y/m/d H:i:s', $time) . " [$level] [$category] $message\n";
$messages = $this->filterMessages($messages);
$this->messages = array_merge($this->messages, $messages);
if ($export && !empty($this->messages)) {
$this->prepareExport($final);
$this->exportMessages($final);
}
}
/**
* 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
* Prepares the [[messages]] for exporting.
* This method will modify each message by prependding extra information
* if [[prefixSession]] and/or [[prefixUser]] are set true.
* It will also add an additional message showing context information if
* [[logUser]] and/or [[logVars]] are set.
* @param boolean $final whether this method is called at the end of the current application
*/
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))
protected function prepareExport($final)
{
if ($this->filter !== null)
Yii::createComponent($this->filter)->filter($this->messages);
$this->processLogs($this->messages);
$this->messages = array();
$prefix = array();
if ($this->prefixSession && ($id = session_id()) !== '') {
$prefix[] = "[$id]";
}
if ($this->prefixUser && ($user = \Yii::app()->getComponent('user', false)) !== null) {
$prefix[] = '[' . $user->getName() . ']';
$prefix[] = '[' . $user->getId() . ']';
}
if ($prefix !== array()) {
$prefix = implode(' ', $prefix);
foreach ($this->messages as $i => $message) {
$this->messages[$i][0] = $prefix . ' ' . $this->messages[$i][0];
}
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'))));
}
if ($final && ($context = $this->getContextMessage()) !== '') {
$this->messages[] = array($context, Logger::LEVEL_INFO, 'application', YII_BEGIN_TIME);
}
}
/**
* Filter function used by {@link getLogs}
* @param array $value element to be filtered
* @return array valid log, false if not.
* 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 filterByCategory($value)
{
foreach ($this->_categories as $category)
protected function getContextMessage()
{
$cat = strtolower($value[2]);
if ($cat === $category || (($c = rtrim($category, '.*')) !== $category && strpos($cat, $c) === 0))
return $value;
$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 false;
}
return implode("\n\n", $context);
}
/**
* Filter function used by {@link getLogs}
* @param array $value element to be filtered
* @return array valid log, false if not.
* Filters the given messages according to their categories and levels.
* @param array $messages messages to be filtered
* @return array the filtered messages.
* @see filterByCategory
* @see filterByLevel
*/
protected function filterByLevel($value)
protected function filterMessages($messages)
{
return in_array(strtolower($value[1]), $this->_levels) ? $value : false;
foreach ($messages as $i => $message) {
if (!empty($this->levels) && !in_array($message[1], $this->levels)) {
unset($messages[$i]);
continue;
}
$matched = empty($this->categories);
foreach ($this->categories as $category) {
$prefix = rtrim($category, '*');
if (strpos($message[2], $prefix) === 0 && ($message[2] === $category || $prefix !== $category)) {
$matched = true;
break;
}
}
if ($matched) {
foreach ($this->excludeCategories as $category) {
$prefix = rtrim($category, '*');
foreach ($messages as $i => $message) {
if (strpos($message[2], $prefix) === 0 && ($message[2] === $category || $prefix !== $category)) {
$matched = false;
break;
}
}
}
}
if (!$matched) {
unset($messages[$i]);
}
}
return $messages;
}
/**
* 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);
* Formats a log message.
* The message structure follows that in [[Logger::messages]].
* @param array $message the log message to be formatted.
* @return string the formatted message
*/
public function formatMessage($message)
{
return @date('Y/m/d H:i:s', $message[3]) . " [{$message[1]}] [{$message[2]}] {$message[0]}\n";
}
}

Loading…
Cancel
Save