日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

Yii源码阅读笔记 - 日志组件

發(fā)布時(shí)間:2024/4/14 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Yii源码阅读笔记 - 日志组件 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

2015-03-09 一

By?youngsterxyf

使用

Yii框架為開(kāi)發(fā)者提供兩個(gè)靜態(tài)方法進(jìn)行日志記錄:

Yii::log($message, $level, $category); Yii::trace($message, $category);

兩者的區(qū)別在于后者依賴于應(yīng)用開(kāi)啟調(diào)試模式,即定義常量YII_DEBUG:

defined('YII_DEBUG') or define('YII_DEBUG', true);

Yii::log方法的調(diào)用需要指定message的level和category。category是格式為“xxx.yyy.zzz”的路徑別名字符串,比如日志是在yii/framework/web/CController類中記錄的,那么category為“system.web.CController”。level應(yīng)為以下幾種之一:

  • trace:Yii::trace方法即是使用的這個(gè)level。用于跟蹤執(zhí)行流
  • info:記錄通用信息日志
  • profile:用于性能分析
  • warning:用于記錄警告日志
  • error:用于記錄重大錯(cuò)誤日志

要想日志真的輸出到文件、郵件、web頁(yè)面等地方,還得為應(yīng)用添加如下配置:

array( ...... 'preload'=>array('log'), 'components'=>array( ...... 'log'=>array( 'class'=>'CLogRouter', 'routes'=>array( array( 'class'=>'CFileLogRoute', 'levels'=>'trace, info', 'categories'=>'system.*', ), array( 'class'=>'CEmailLogRoute', 'levels'=>'error, warning', 'emails'=>'admin@example.com', ), ), ), ), )

注冊(cè)使用名為log的組件,組件對(duì)應(yīng)的類為CLogRouter(見(jiàn)類文件yii/framework/logging/CLogRouter.php),并且需要為組件提供參數(shù)routes, 從目錄yii/framework/logging可以看到可使用的日志輸出目標(biāo)路由有:CDbLogRoute(將日志記錄到數(shù)據(jù)庫(kù)中)、CEmailLogRoute(將日志發(fā)送到郵箱)、CFileLogRoute(記錄到文件中)、CWebLogRoute(將日志顯示在對(duì)應(yīng)的網(wǎng)頁(yè)中)、CProfileLogRoute,其中CProfileLogRoute直接繼承自CWebLogRoute,其他路由類都直接繼承自CLogRoute類。

至于為什么需要對(duì)log組件進(jìn)行preload,即預(yù)先實(shí)例化,后邊再說(shuō)。

分析

先來(lái)看看Yii::log和Yii::trace的實(shí)現(xiàn):

/** * Writes a trace message. * This method will only log a message when the application is in debug mode. * @param string $msg message to be logged * @param string $category category of the message * @see log */ public static function trace($msg,$category='application') { // 得先定義常量YII_DEBUG為true if(YII_DEBUG) // CLogger::LEVEL_TRACE self::log($msg,CLogger::LEVEL_TRACE,$category); } /** * Logs a message. * Messages logged by this method may be retrieved via {@link CLogger::getLogs} * and may be recorded in different media, such as file, email, database, using * {@link CLogRouter}. * @param string $msg 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. */ public static function log($msg,$level=CLogger::LEVEL_INFO,$category='application') { if(self::$_logger===null) self::$_logger=new CLogger; // 注意這里的常量YII_TRACE_LEVEL,如果想日志中含有對(duì)應(yīng)文件名、對(duì)應(yīng)行,那么應(yīng)該定義YII_TRACE_LEVEL大于0, // 這個(gè)常量的意思應(yīng)該是日志追蹤的深度 if(YII_DEBUG && YII_TRACE_LEVEL>0 && $level!==CLogger::LEVEL_PROFILE) { $traces=debug_backtrace(); $count=0; foreach($traces as $trace) { if(isset($trace['file'],$trace['line']) && strpos($trace['file'],YII_PATH)!==0) { $msg.="\nin ".$trace['file'].' ('.$trace['line'].')'; if(++$count>=YII_TRACE_LEVEL) break; } } } // 調(diào)用的是CLogger類的log方法 self::$_logger->log($msg,$level,$category); }

CLogger類的log方法實(shí)現(xiàn)如下所示:

/** * 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++; // autoFlush的默認(rèn)值為10000,即只有當(dāng)日志的條數(shù)達(dá)到10000(或請(qǐng)求處理結(jié)束時(shí)),才會(huì)flush到輸出,否則一直將日志存放在內(nèi)存中 if($this->autoFlush>0 && $this->_logCount>=$this->autoFlush && !$this->_processing) { $this->_processing=true; // autoDump默認(rèn)為false $this->flush($this->autoDump); $this->_processing=false; } }

CLogger類的flush方法實(shí)現(xiàn)如下所示:

/** * 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) { // 事件對(duì)象中會(huì)帶有當(dāng)前的CLogger對(duì)象,作為事件的發(fā)送者 // 但在類CLogRouter的方法collectLogs和processLogs中并沒(méi)有使用這個(gè)CLogger對(duì)象, // 而是通過(guò)Yii::getLogger()來(lái)得到同一個(gè)CLogger對(duì)象,為什么不直接使用呢? $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) { // 拋出onFlush事件 // raiseEvent方法定義在CComponent類中,CLogger類繼承自CComponent類 $this->raiseEvent('onFlush', $event); }

CComponent類的raiseEvent方法實(shí)現(xiàn)如下所示:

/** * 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 undefined or an event handler is invalid. */ public function raiseEvent($name,$event) { $name=strtolower($name); if(isset($this->_e[$name])) { // 逐個(gè)執(zhí)行與事件$name綁定的$handler foreach($this->_e[$name] as $handler) { if(is_string($handler)) // 會(huì)將事件對(duì)象(CEvent對(duì)象)傳到$handler中 call_user_func($handler,$event); elseif(is_callable($handler,true)) { if(is_array($handler)) { // an array: 0 - object, 1 - method name list($object,$method)=$handler; if(is_string($object)) // static method call 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}'=>get_class($this), '{event}'=>$name, '{handler}'=>$handler[1]))); } else // PHP 5.3: anonymous function call_user_func($handler,$event); } else 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)) throw new CException(Yii::t('yii','Event "{class}.{event}" is not defined.', array('{class}'=>get_class($this), '{event}'=>$name))); }

看到這里,你可能會(huì)很奇怪怎么都沒(méi)看到真正記錄日志的代碼呢?在與事件綁定的各個(gè)$handler里呢,這也是log組件的routes參數(shù)配置成數(shù)組的原因,$handle就是該數(shù)組中對(duì)應(yīng)類的實(shí)例化對(duì)象的日志記錄方法。

那么這些$handle是在哪里綁定事件的呢?

既然log組件對(duì)應(yīng)的是CLogRouter類,那么來(lái)看看其實(shí)現(xiàn)。

類CLogRouter繼承自類CApplicationComponent。根據(jù)Yii源碼閱讀筆記 - 組件集成一文,可知組件初始化時(shí)實(shí)例化對(duì)象會(huì)調(diào)用init方法來(lái)完成一些初始化操作,類CLogRouter的init方法實(shí)現(xiàn)如下所示:

/** * Initializes this application component. * This method is required by the IApplicationComponent interface. */ public function init() { parent::init(); // 實(shí)例化配置的routes參數(shù)中指定的日志路由類列表 foreach($this->_routes as $name=>$route) { $route=Yii::createComponent($route); $route->init(); $this->_routes[$name]=$route; } // 將當(dāng)前對(duì)象的collectLogs方法綁定到事件onFlush Yii::getLogger()->attachEventHandler('onFlush',array($this,'collectLogs')); // 將當(dāng)前對(duì)象的processLogs方法綁定到事件onEndRequest(表示請(qǐng)求處理結(jié)束?) Yii::app()->attachEventHandler('onEndRequest',array($this,'processLogs')); }

而類CLogRouter的方法collectLogs和processLogs實(shí)現(xiàn)如下所示:

/** * 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']; // 遍歷所有日志路由對(duì)象,執(zhí)行其方法collectLogs foreach($this->_routes as $route) { // 屬性enabled默認(rèn)值為true if($route->enabled) // $dumpLogs默認(rèn)為false,由事件對(duì)象傳過(guò)來(lái) $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(); // 遍歷所有日志路由對(duì)象,執(zhí)行其方法collectLogs foreach($this->_routes as $route) { if($route->enabled) // 注意這里參數(shù)$dumpLogs參數(shù)值始終為true $route->collectLogs($logger,true); } }

以類CWebLogRoute為例來(lái)看看日志路由類的方法collectLogs,該方法定義于類CLogRoute中,實(shí)現(xià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 */ // 事件onFlush觸發(fā)時(shí),傳入的$processLogs參數(shù)值默認(rèn)為false,事件onEndRequest觸發(fā)時(shí)為true public function collectLogs($logger, $processLogs=false) { // 從類CLogger實(shí)例化對(duì)象的_logs屬性值中過(guò)濾得到目標(biāo)日志記錄 $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()) // 調(diào)用實(shí)際route的processLogs方法 $this->processLogs($this->logs); $this->logs=array(); } }

而類CWebLogRoute的processLogs方法實(shí)現(xiàn)如下所示:

/** * 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(); // 用firebug來(lái)顯示日志信息的話? 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; // 渲染yii/framework/views/log.php(log-firebug.php),將結(jié)果放在實(shí)際請(qǐng)求頁(yè)面內(nèi)容的下面 $viewFile=YII_PATH.DIRECTORY_SEPARATOR.'views'.DIRECTORY_SEPARATOR.$view.'.php'; include($app->findLocalizedFile($viewFile,'en')); }

從上述代碼可以知道日志路由類是將日志信息按照一定格式顯示在實(shí)際請(qǐng)求頁(yè)面內(nèi)容的下方。

再來(lái)看看方法init中調(diào)用的兩個(gè)attachEventHandler,它們的定義是同一個(gè),定義在類CComponent中(類CLogger直接繼承自類CComponent),實(shí)現(xiàn)如下所示:

public function attachEventHandler($name,$handler) { // 將事件處理器$handler加到處理器列表中,在事件發(fā)生時(shí)會(huì)逐個(gè)處理器觸發(fā)執(zhí)行 $this->getEventHandlers($name)->add($handler); }

其中方法EventHandlers的實(shí)現(xiàn)如下所示:

/** * 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=strtolower($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))); }

從上述分析可以知道所謂事件系統(tǒng),其實(shí)就是將處理函數(shù)/對(duì)象方法放到與事件ID對(duì)應(yīng)的一個(gè)列表中,然后在事件觸發(fā)時(shí),逐個(gè)調(diào)用執(zhí)行這些函數(shù)/對(duì)象方法。

Yii框架基于事件系統(tǒng),可以做到同時(shí)將日志信息寫到多個(gè)目標(biāo)輸出中。


回到之前提到的那個(gè)問(wèn)題:為什么需要對(duì)log組件進(jìn)行preload?

這是因?yàn)?#xff1a;對(duì)于日志組件的使用并不是通過(guò)Yii::app()->db這種形式來(lái)調(diào)用的(如果基于這種形式,那么就可以在首次調(diào)用時(shí)再做組件實(shí)例化,以實(shí)現(xiàn)組件延遲加載),而是通過(guò)觸發(fā)事件來(lái)間接調(diào)用,但這就需要在事件觸發(fā)之間將相關(guān)的處理函數(shù)/對(duì)象方法綁定到事件,這個(gè)綁定操作又是在日志log組件的init方法中執(zhí)行的,一般組件類實(shí)例化時(shí)才會(huì)調(diào)用其init方法,所以需要對(duì)log組件進(jìn)行預(yù)加載。

參考

  • Yii - topics - Logging

轉(zhuǎn)載于:https://www.cnblogs.com/sunscheung/p/4864179.html

總結(jié)

以上是生活随笔為你收集整理的Yii源码阅读笔记 - 日志组件的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 久久国产精品精品国产色婷婷 | av免费播放网站 | 天天做天天爱天天做 | 夜夜高潮夜夜爽 | 亚洲性生活大片 | 欧美黄色免费看 | 黄色免费成人 | 天堂男人在线 | 国产精品探花一区二区三区 | 美女诱惑av| 黄色片久久久久 | 色噜噜在线播放 | av一二三 | 性网址| 伊人在线视频 | 91精品国产99久久久久久 | 一级片视频免费看 | 中文字幕日韩三级 | 91一区在线 | 亚洲中文字幕无码不卡电影 | 六月色 | 久久亚洲精少妇毛片午夜无码 | 一区二区三区亚洲精品 | 欧美性天天影院 | 丰满秘书被猛烈进入高清播放在 | 人妖ts福利视频一二三区 | 性生活一级大片 | 国产一级内谢 | 全部毛片永久免费看 | 国产精品三级在线 | 老司机深夜福利影院 | xxxxwwww在线观看 | 波多野结衣一区二区在线 | 亚洲成人无码久久 | 伊人久久久久久久久久久久 | 51久久| av成人资源 | 91极品视频 | 爱爱爱爱网 | 主播福利在线 | 91性高潮久久久久久久久 | 美女三区 | 一本一道av无码中文字幕 | 影音先锋中文字幕一区二区 | 精品人妻一区二区三区免费看 | 波多野42部无码喷潮在线 | 西西4444www大胆无码 | 日韩操操操 | 色狠狠操| 99re在线视频免费观看 | 亚洲精品乱码久久久久久蜜桃麻豆 | 美女狂揉羞羞的视频 | 亚洲精品乱码久久久久久蜜桃图片 | 国产精品一区视频 | 亚洲熟女乱综合一区二区 | 日韩美女视频一区二区 | 亚洲一区二区三区四区电影 | 黄色最新网址 | 一区二区三区在线观看av | 中国美女囗交视频 | 五月天狠狠干 | 日韩精品一区二区三区在线 | 欧美性三级 | 国产中文久久 | 国产在线精品一区二区 | 自拍偷拍99 | av在线资源 | 免费福利在线视频 | 亚洲黄a| 国产精品久久久久一区二区三区 | 婷婷精品一区二区三区 | 成人伊人网| 欧美激情15p| 一本色道久久88加勒比—综合 | 日本黄页网址 | 中文久草 | 另类少妇人与禽zozz0性伦 | 欧美顶级黄色大片免费 | 国产亚洲午夜 | 中文字幕日韩一区 | 日本偷偷操 | 亚洲天堂成人 | 国产黑丝在线 | 日韩一区二区三区在线观看视频 | 欧美三级视频在线播放 | 久久香蕉网站 | 国产裸体网站 | 国产三级在线 | 熟妇高潮一区二区 | 精品人妻无码一区二区三区 | 中文字幕欧美一区 | 成年人在线观看视频 | 亚洲精品一卡二卡 | 美女隐私直播 | 欧美女同视频 | 91精品国产91久久久久久 | 秋霞av鲁丝片一区二区 | 亚洲色图第一区 | 手机av不卡 |