您好,登錄后才能下訂單哦!
這篇文章將為大家詳細講解有關詳解Yii框架中的日志功能,文章內容質量較高,因此小編分享給大家做個參考,希望大家閱讀完這篇文章后對相關知識有一定的了解。
Yii頁面級日志開啟
在 Main.php中 log段添加、
下面顯示頁面日志 array( 'class'=>'CWebLogRoute', 'levels'=>'trace', //級別為trace 'categories'=>'system.db.*' //只顯示關于數據庫信息,包括數據庫連接,數據庫執行語句 ),
完整如下:
'log'=>array( 'class'=>'CLogRouter', 'routes'=>array( array( 'class'=>'CFileLogRoute', 'levels'=>'error, warning', ), // 下面顯示頁面日志 array( 'class'=>'CWebLogRoute', 'levels'=>'trace', //級別為trace 'categories'=>'system.db.*' //只顯示關于數據庫信息,包括數據庫連接,數據庫執行語句 ), // uncomment the following to show log messages on web pages /* array( 'class'=>'CWebLogRoute', ), */ ), ),
擴展 Yii2 自帶的日志組件
<?php /** * author : forecho <caizhenghai@gmail.com> * createTime : 2015/12/22 18:13 * description: */ namespace common\components; use Yii; use yii\helpers\FileHelper; class FileTarget extends \yii\log\FileTarget { /** * @var bool 是否啟用日志前綴 (@app/runtime/logs/error/20151223_app.log) */ public $enableDatePrefix = false; /** * @var bool 啟用日志等級目錄 */ public $enableCategoryDir = false; private $_logFilePath = ''; public function init() { if ($this->logFile === null) { $this->logFile = Yii::$app->getRuntimePath() . '/logs/app.log'; } else { $this->logFile = Yii::getAlias($this->logFile); } $this->_logFilePath = dirname($this->logFile); // 啟用日志前綴 if ($this->enableDatePrefix) { $filename = basename($this->logFile); $this->logFile = $this->_logFilePath . '/' . date('Ymd') . '_' . $filename; } if (!is_dir($this->_logFilePath)) { FileHelper::createDirectory($this->_logFilePath, $this->dirMode, true); } if ($this->maxLogFiles < 1) { $this->maxLogFiles = 1; } if ($this->maxFileSize < 1) { $this->maxFileSize = 1; } } }
在配置文件中這樣使用:
'components' => [ 'log' => [ 'traceLevel' => YII_DEBUG ? 3 : 0, 'targets' => [ /** * 錯誤級別日志:當某些需要立馬解決的致命問題發生的時候,調用此方法記錄相關信息。 * 使用方法:Yii::error() */ [ 'class' => 'common\components\FileTarget', // 日志等級 'levels' => ['error'], // 被收集記錄的額外數據 'logVars' => ['_GET', '_POST', '_FILES', '_COOKIE', '_SESSION','_SERVER'], // 指定日志保存的文件名 'logFile' => '@app/runtime/logs/error/app.log', // 是否開啟日志 (@app/runtime/logs/error/20151223_app.log) 'enableDatePrefix' => true, 'maxFileSize' => 1024 * 1, 'maxLogFiles' => 100, ], /** * 警告級別日志:當某些期望之外的事情發生的時候,使用該方法。 * 使用方法:Yii::warning() */ [ 'class' => 'common\components\FileTarget', // 日志等級 'levels' => ['warning'], // 被收集記錄的額外數據 'logVars' => ['_GET', '_POST', '_FILES', '_COOKIE', '_SESSION','_SERVER'], // 指定日志保存的文件名 'logFile' => '@app/runtime/logs/warning/app.log', // 是否開啟日志 (@app/runtime/logs/warning/20151223_app.log) 'enableDatePrefix' => true, 'maxFileSize' => 1024 * 1, 'maxLogFiles' => 100, ], /** * info 級別日志:在某些位置記錄一些比較有用的信息的時候使用。 * 使用方法:Yii::info() */ [ 'class' => 'common\components\FileTarget', // 日志等級 'levels' => ['info'], // 被收集記錄的額外數據 'logVars' => ['_GET', '_POST', '_FILES', '_COOKIE', '_SESSION','_SERVER'], // 指定日志保存的文件名 'logFile' => '@app/runtime/logs/info/app.log', // 是否開啟日志 (@app/runtime/logs/info/20151223_app.log) 'enableDatePrefix' => true, 'maxFileSize' => 1024 * 1, 'maxLogFiles' => 100, ], /** * trace 級別日志:記錄關于某段代碼運行的相關消息。主要是用于開發環境。 * 使用方法:Yii::trace() */ [ 'class' => 'common\components\FileTarget', // 日志等級 'levels' => ['trace'], // 被收集記錄的額外數據 'logVars' => ['_GET', '_POST', '_FILES', '_COOKIE', '_SESSION','_SERVER'], // 指定日志保存的文件名 'logFile' => '@app/runtime/logs/trace/app.log', // 是否開啟日志 (@app/runtime/logs/trace/20151223_app.log) 'enableDatePrefix' => true, 'maxFileSize' => 1024 * 1, 'maxLogFiles' => 100, ], ], ], ],
yii日志的邏輯
Yii使用層次的日志處理機制,即日志的收集與日志最終的處理(如顯示、保存到文件、保存到數據數)是分離的。
日志信息的收集由CLogger(日志記錄器)完成,而日志信息的分發處理,則在CLogRouter的調度(稱為日志路由管理器)下,分發給處理對象(如CFileLogRoute以及logging目錄下繼承自CLogRoute的類, 稱為日志處理器),經過反復閱讀其源代碼,我更是為Yii的設計思想所折服,如此的分層處理,使得其易于靈活擴展。
而日志信息有級別之分,如普通的info, profile, trace, warning, error級別,可以在日志路由中設置過慮條件,如設置CFileRoute的levels屬性,即可只處理指定級別的日志信息。
如在程序中調用:
Yii::log($message,CLogger::LEVEL_ERROR,$category);
對應的流程可能如下:
生成CLogger實例
如果YII_DEBUG , YII_TRACE_LEVEL都已經定義為有效值,并且日志級別不是profile, 則產生調用回溯信息, 并追加到日志信息上。
調用CLogger:: log($msg,$level,$category) 收集日志,實際上這時日志并沒有寫入文件,僅僅是暫存于內存之中。
問題:日志是在何時被寫入文件的?
經過反復跟蹤,我發現在CLogRouter類的init方法中為Application對象的OnEndRequest事件綁定處理器CLogRouter::processLogs()。同時也給Yii::$_logger的onFlush事件綁定事件處理器CLogRouter::collectLogs方法,用于在Yii::log()中當日志消息量過多時,及時將日志刷新寫入文件。代碼如下:
/** * 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'));}
而在CApplication::run()方法中定義了:
if($this->hasEventHandler('onEndRequest')) { $this->onEndRequest(new CEvent($this)); }
到這里我們可以理解CLogger (Yii::$_logger)僅僅是將日志進行收集(記錄到內容結構之中),然后在程序結束時,由$app對象調用CLogRouter的processLogs進行日志的處理。Yii支持日志多道路由,比如:同一份日志即可寫入至文件,又可顯示到頁面上,甚至同時以電子郵件發送,更甚至同時記錄到數據庫中,這是由配置文件中的log:routes配置實現的,為log:routes配置多個元素,實現多個路由分發。日志信息的過濾,記錄均是由最終的日志處理器處理。
日志處理器要完成的任務主要包含以下幾點: 從CLogger中取得所有日志,并進行過濾(主要是levels, categories兩項定義log:routes:levels/categories)
先進行過濾參考CFileLogRoute::collectLogs()中的邏輯:
$logs=$logger->getLogs($this->levels,$this->categories); //執行過濾,只得到期望信息
日志過濾已經完成接下來就要對日志進行最終處理(如寫入到文件,記錄至數據庫等)
CFileLogRoute::processLogs($logs);
但這個函數之中,有個小bug, 只判斷日志目錄是否可寫,沒有判斷日志文件本身是否可寫.CFileLogRoute實現了類似Linux的日志輪換功能(LogRoate), 并規定了日志文件的大小,考慮得很周到,很完善! 我也要向其學習并吸收其思想!
protected/config/main.php中的配置:
'preload'=>array('log'), components => array( 'log'=>array( 'class'=>'CLogRouter', 'routes'=>array( array( 'class'=>'CFileLogRoute', 'levels'=>'error, warning,trace', ), ) ) )
定義log組件需要預先加載(實例化)。配置使用CLogRouter作為日志路由管理器,并設置了其日志路由處理器(routes屬性)及其配置屬性。而preload, log屬性的定義,均要應用到CWebApplication對象上(請參閱CApplication::__construct中的configure調用, configure從CModule繼承而來)。而在CWebApplication的構造函數中執行preloadComponents(),就創建了log對象(即CLogRouter的實例)。
創建并初始化一個組件時,實際上調用的是CModule::getComponent, 這個調用中使用YiiBase::createComponent創建組件對象,并再調用組件的init初始化之。
再閱讀CLogRouter::init()過程,在這里有兩個關鍵之處,一是創建日志路由處理器(即決定日志的最終處理方式:寫入文件,郵件發送等等),二是給應用程序對象綁定onEndRequest事件處理CLogRouter::processLogs()。而在CApplication::run()確實有相關代碼用于運行onEndRequest事件處理句柄:
if($this->hasEventHandler('onEndRequest')) { $this->onEndRequest(new CEvent($this)); }
也就是說,日志的最終處理(比如寫入文件,系統日志,發送郵件)是發生在應用程序運行完畢之后的。Yii使用事件機制,巧妙地實現了事件與處理句柄的關聯。
也就是說,當應用程序運行完畢,將執行CLogRouter::processLogs,對日志進行處理,。CLogRouter被稱之為日志路由管理器。每個日志路由處理器從CLooger對象中取得相應的日志(使用過濾機制),作最終處理。
具體而言Yii的日志系統,分為以下幾個層次:
日志發送者,即程序中調用Yii::log($msg, $level, $category),將日志發送給CLogger對象
CLogger對象負責將日志記錄暫存于內存之中程序運行結束后,log組件(日志路由管理器CLogRoute)的processLogs方法被激活執行,由其逐個調用日志路由器,作日志的最后處理。
更為詳細的大致過程如下:
CApplication::__construct()中調用preloadComponents, 這導致log組件(CLogRoute)被實例化,并被調用init方法初始化。
log組件(CLogRoute)的init方法中,其是初始化日志路由,并給CApplication對象onEndRequest事件綁定處理流程processLogs。給CLooger組件的onFlush事件綁定處理流程collectLogs。
應用程序的其它部分通過調用Yii::log()向CLogger組件發送日志信息,CLogger組件將日志信息暫存到內存中。
CApplication執行完畢(run方法中),會激活onEndRequest事件,綁定的事件處理器processLogs被執行,日志被寫入文件之中。 Yii的日志路由機制,給日志系統擴展帶來了無限的靈活。并且其多道路由處理機制,可將同一份日志信息進行多種方式處理。
這里舉出一個案例:發生error級別的數據庫錯誤時,及時給相關維護人員發送電子郵件,并同時將這些日志記錄到文件之中。規劃思路,發送郵件和手機短信是兩個不同的功能,Yii已經帶了日志郵件發送組件(logging/CEmailLogRoute.php),但這個組件中卻使用了php自帶的mail函數,使用mail函數需要配置php.ini中的smtp主機,并且使用非驗證發送方式,這種方式在目前的實際情況下已經完全不可使用。代替地我們需要使用帶驗證功能的smtp發送方式。在protected/components/目錄下定義日志處理器類myEmailLogRoute,并讓其繼承自CEmailLogRoute,最主要的目的是重寫CEmailLogRoute::sendEmail()方法 ,其中,SMTP的處理細節請自行完善(本文的重點是放在如何處理日志上,而不是發送郵件上)。
接下來,我們就可以定義日志路由處理,編輯protected/config/main.php, 在log組件的routes組件添加新的路由配置:
'log'=>array( 'class'=>'CLogRouter', 'routes'=>array( array( 'class'=>'CFileLogRoute', 'levels'=>'error, warning,trace', ), array( 'class' => 'myEmailLogRoute', 'levels' => 'error', #所有異常的錯誤級別均為error, 'categories' => 'exception.CDbException', #數據庫產生錯誤時,均會產生CDbException異常。 'host' => 'mail.163.com', 'port' => 25, 'user' => 'jeff_yu', 'password' => 'you password', 'timeout' => 30, 'emails' => 'jeff_yu@gmail.com', #日志接收人。 'sentFrom' => 'jeff_yu@gmail.com', ),
關于詳解Yii框架中的日志功能就分享到這里了,希望以上內容可以對大家有一定的幫助,可以學到更多知識。如果覺得文章不錯,可以把它分享出去讓更多的人看到。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。