? using
The YII framework provides developers with two static methods for logging:
Yii::log ($message, $level, $category); Yii::trace ($message, $category);
The difference is that the latter relies on the application to turn on debug mode, which defines the constant yii_debug:
Defined (' Yii_debug ') or define (' Yii_debug ', true);
The invocation of the Yii::log method requires specifying the level and category of the message. Category is a path alias string in the format "xxx.yyy.zzz", such as the log is recorded in the Yii/framework/web/ccontroller class, then the category is "System.web.CController". Level should be one of the following:
The Trace:yii::trace method is the level that is used. Used to track the execution flow
Info: Record Common Information log
Profile: For performance analysis
Warning: Used to log warning logs
Error: Used to log critical error logs
To make the log really output to a file, mail, Web page, and so on, you have to add the following configuration for the app:
Array (  , ... ' preload ' =>array (' log '), ' components ' =>array ( &NBSP, ... ' log ' =>array ( ' class ' = ' CLo Grouter ', ' routes ' =>array ( &N Bsp;array ( ' class ' = ' Cfilelogroute ', ' levels ' + ' trace, info ', &NB Sp ' categories ' + ' system.* ', ), & nbsp array ( ' Clas s ' = ' cemaillogroute ', ' levels ' + ' ERROR, Warnin G ',   ' emails ' = ' [email protected] ', ), ), ), ),
Register to use a component named Log, the corresponding class for the component is Clogrouter (see class file yii/framework/logging/clogrouter.php), and you need to provide parameters routes for the component, from the directory yii/framework/ Logging can see the log output target routes that can be used: CDbLogRoute
(logging to a database), CEmailLogRoute
(sending logs to a mailbox), ( CFileLogRoute
logging to a file), CWebLogRoute
(displaying the logs in the corresponding Web page) CProfileLogRoute
, Where Cprofilelogroute inherits directly from Cweblogroute, other routing classes inherit directly from the Clogroute class.
As for why the log component needs to be preload, that is, pre-instantiation, and so on.
Analysis
Let's take a look at the implementation of Yii::log and Yii::trace:
/** * writes a trace message. * This method would only log a message when the application are in debug mode. * @param string $msg message to being logged * @param string $category category of the message * @see log */public static fun Ction Trace ($msg, $category = ' application ') { //must first define a constant Yii_debug to true if (yii_debug) //clogger::level_trace self::log ($msg, Clogger::level_trace, $category) ;} /** * Logs a message. * Messages logged by this method is retrieved via {@link clogger::getlogs} * and May is recorded in different media, s Uch as file, email, database, using * {@link clogrouter}. * @param string $msg message to being 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. */public static function log ($msg, $level =clogger::level_info, $category = ' application ') { &Nbsp;if (self::$_logger===null) self::$_logger=new CLogger; //Note Here the constant Yii_trace_level, if you want the log to contain the corresponding file name, the corresponding row, then you should define yii_trace_level greater than 0, // This constant should mean the depth of log tracking if (Yii_debug && yii_trace_level>0 && $level!==clogger::level_ Profile) { $traces =debug_backtrace (), $count =0; foreach ($traces as $trace) {  IF (i Sset ($trace [' file '], $trace [' line '] && strpos ($trace [' file '],yii_path)!==0) { $msg. = "\nin" $trace [' file ']. ' ('. $trace [' line ']. ') '; if (+ + $count >=yii_trace_level) break; } &nbsP } } //calls the Clogger class's log method self::$_logger->log ($msg, $level, $category);}
The
Clogger class's Log method implementation is as follows:
/** * Logs a message. * Messages logged by this method is retrieved back via {@link getlogs}. * @param string $message message to being 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++; The default value for //AutoFlush is 10000, which is to flush to the output only if the number of logs reaches 10000 (or at the end of request processing), otherwise the log is kept in memory if ($this Autoflush>0 && $this->_logcount>= $this->autoflush && $this->_processing) { $this->_processing=true; //Autodump default = False &nbs P $this->flush ($this->autodump); $this->_processing=false; }}
The Flush method implementation of the
Clogger class is as follows:
/** * Removes all recorded messages from the memory. * This method would raise an {@link Onflush} event. * The attached event handlers can process the log messages before they is removed. * @param Boolean $dumpLogs whether to process the logs immediately as they is passed to log route * @since 1.1.0 */public function Flush ($dumpLogs =false) { //event object with the current Clogger object as the sender of the event // But in the class Clogrouter method Collectlogs and Processlogs do not use this Clogger object, //instead of Yii::getlogger () to get the same Clogger object, Why not use it directly? $this->onflush ($this, Array (' dumplogs ' = $dumpLogs)); //emptying reset $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) { //throws Onflush Events & nbsp The //RaiseEvent method is defined in the Ccomponent class, and the Clogger class inherits from the Ccomponent class $this->raiseevent (' Onflush ', $event);}
The RaiseEvent method implementation of the
Ccomponent class is as follows:
/** * raises an event. * This method represents the happening of an event. It invokes * All attached handlers for the event. * @param string $name The event name * @param CEvent $event the event parameter * @throws CException if the event is undef Ined or an event handler is invalid. */public function RaiseEvent ($name, $event) { $name =strtolower ($name); if (Isset ($this->_e [$name])) { //one-by-one with event $name binding $handler foreach ($this->_e[$ Name] as $handler) { if (is_string ($handler)) &N Bsp //the Event object (CEvent object) to $handler & Nbsp;call_user_func ($handler, $event); elseif (is_callable ($handler, True)) {&NB Sp &NBSP;IF (Is_array($handler)) { &N bsp;//an array:0-object, 1-method name list ($ob ject, $method) = $handler; if (is_string ($object)) //static method call &N Bsp call_user_func ($handler, $event); elseif (method_exists ($object, $method)) , $object-$method ($event); else throw new CException (yii::t (' Yii ', ' Event ' {class}.{ Event} "is attached with an invalid handler" {handler} ".', array (' {class} ' =>ge T_class ($this), ' {event} ' = $name, ' {handler} ' and ' = $handler [1])); &NBSP,} &NBSP;ELSE//PH P 5.3:anonymous function call_user_func ($handler, $ev ENT); &NBSP,} else &NBSP ; throw New CException (yii::t (' Yii ', ' Event ' {class}.{ Event} "is attached with an invalid handler" {handler} ". ', array (' {class} ' =>get_class ($this), ' {event} ' = = $name, ' {handler} ' =>gettype ($handler))); //stop further handling if param.handled is set true if (($event instanceof CEvent) && $event->handled) return; } } elseif (yii_debug &&! $this->hasevent ($name)) & nbsp throw New CException (yii::t (' Yii ', ' Event ' {class}.{ Event} "is not defined. ', array (' {class} ' =>get_class ($this), ' {event} ' =>$ name)));}
Seeing here, you might be surprised how you didn't see the code that really recorded the log? In each $handler that is bound to the event, this is also the reason why the routes parameter of the log component is configured as an array, $handle is the logging method for the instantiated object of the corresponding class in the arrays.
So where are these $handle bound events?
Now that the log component corresponds to the Clogrouter class, take a look at its implementation.
Class Clogrouter inherit from class Capplicationcomponent. According to the Yii source reading notes-component integration article, we know that the instantiation object will call the Init method to complete some initialization operations when the component is initialized, and the Init method of class Clogrouter is as follows:
/** * Initializes this application component. * This method was required by the Iapplicationcomponent interface. */public function init () {parent::init (); The list of log routing classes specified in the routes parameter of the instantiated configuration is foreach ($this->_routes as $name = = $route) {$route =yii::createcomponent ($ro UTE); $route->init (); $this->_routes[$name]= $route; }//Bind the Collectlogs method of the current object to the event Onflush Yii::getlogger ()->attacheventhandler (' Onflush ', Array ($this, ' collectlogs ') ); Binds the Processlogs method of the current object to the event Onendrequest, which indicates the end of the request processing? ) Yii::app ()->attacheventhandler (' Onendrequest ', Array ($this, ' processlogs '));}
The methods of class Clogrouter Collectlogs and Processlogs implementations are as follows:
/** * Collects log messages from a logger. * This method is a event handler to the {@link Clogger::onflush} event. * @param CEvent $event event parameter */public function collectlogs ($event) { $logger =yii::getlogger (); &nbs P $dumpLogs =isset ($event->params[' dumplogs ') && $event->params[' dumplogs ']; //traverses all log routing objects, executes its methods collectlogs foreach ($this->_routes as $route) { & nbsp //Property enabled default value is True if ($route->enabled) &NB sp;//$dumpLogs default to False, transmitted by event object $route->collectlogs ($logger, $dumpLogs); }}/** * Collects and processes log messages from a logger. * This method is a event handler to the {@link capplication::onendrequest} event. * @param CEvent $event Event parameter * @since 1.1.0 */public function processlogs ($event) { $logger =yii::get Logger (); //Traverse all log routing objects, execute their methods collectlogs foreach ($this->_routes as $route) { if ($route->enabled) //note here the parameter $dumplogs parameter value is always true & nbsp $route->collectlogs ($logger, true); }}
The
takes class Cweblogroute as an example to look at the method Collectlogs of the log routing class, which is defined in class Clogroute, as follows:
/** * Retrieves filtered log messages from logger for further processing. * @param CLogger $logger Logger Instance * @param boolean $processLogs whether to process the logs after they is collecte When the D from the Logger *///event Onflush triggered, the incoming $processlogs parameter value defaults to False, and the event onendrequest is triggered when Truepublic function Collectlogs ($ Logger, $processLogs =false) { //filtered from the _logs attribute value of the class Clogger instantiation object to get target logging $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) { &NBSP;IF ($this Filter!==null) yii::createcomponent ($this->filter)->filter ($this, logs); if ($this->logs!==array ()) // Call the Processlogs method of the actual route $this->PROcesslogs ($this->logs); $this->logs=array (); }}
The
and class Cweblogroute Processlogs method implementation is as follows:
/** * 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 being located under Framework/data/views. * @param array $data data to being passed to the view */protected function render ($view, $data) { $app =yii::app (); $isAjax = $app->getrequest ()->getisajaxrequest (); $isFlash = $app->getrequest ()->getisflashrequest (); //use Firebug to display log messages? if ($this->showinfirebug) { //do not output anything for Ajax and/ or flash requests if needed if ($isAjax && $this->ignoreajaxinfirebug | | $isFlash &am p;& $this->ignoreflashinfirebug) return; $view. = '-firebug '; &NBSp if (($userAgent = $app->getrequest ()->getuseragent ())!==null && preg_match ('/msie [5-9]/i ', $ useragent) { echo ' <script type= "Text/javascript" & gt; '; echo file_get_contents (dirname (__file__). /.. /vendors/console-normalizer/normalizeconsole.min.js '); echo "</script>\n"; } } elseif (! $app instanceof cwebapplication) | | $isAjax | | $isFlash) return; //renders yii/framework/views/log.php (log-firebug.php), placing the results below the actual requested page content $viewFile =yii_path. Directory_separator. ' Views '. Directory_separator. $view. PHP '; include ($app->findlocalizedfile ($viewFile, ' en '));}
From the above code you can know that the log routing class is to display the log information in a certain format below the content of the actual request page.
Let's take a look at the method. The two attacheventhandler called in Init, whose definition is the same, is defined in class Ccomponent (class Clogger directly inherits from class Ccomponent), as follows:
The Public Function Attacheventhandler ($name, $handler) {//Adds the event processor $handler to the processor list, triggering execution on a per-processor basis when the event occurs $this->geteventha Ndlers ($name)->add ($handler);}
The implementation of the method Eventhandlers is as follows:
/** * Returns The list of attached event handlers for an event. * @param string $name The event name * @return CList List of attached event handlers for the event * @throws CException if The event is not defined */public function geteventhandlers ($name) {if ($this->hasevent ($name)) {$name =st Rtolower ($name); if (!isset ($this->_e[$name)) $this->_e[$name]=new CList; return $this->_e[$name]; } else throw new CException (Yii::t (' Yii ', ' Event ' {class}.{ Event} "is not defined. ', Array (' {class} ' =>get_class ($this), ' {event} ' = $name)));}
From the above analysis you can know that the so-called event system, in fact, the processing function/object method is placed in a list corresponding to the event ID, and then, when the event is triggered, call the execution of these function/object methods.
The YII framework is based on the event system and can be used to write log information to multiple target outputs at the same time.
Go back to the question that you mentioned earlier: Why do I need to preload the log component?
This is because the use of the log component is not Yii::app()->db
invoked in this form (if based on this form, the component instantiation can be done on the first call to implement component lazy loading), but rather by triggering an event to invoke indirectly, but this requires the related handler function between event firings/ Object methods are bound to events, and this binding operation is performed in the Init method of the log component, and the Init method is called when the generic component class is instantiated, so the log component needs to be preloaded.
Reference
Yii Source Reading notes-log components