當前位置:首頁 > PHP教程 > php高級應用 > 列表

全面解讀PHP的Yii框架中的日志功能

發布:smiling 來源: PHP粉絲網  添加日期:2019-11-17 18:41:40 瀏覽: 評論:0 

Yii頁面級日志開啟

在 Main.php中 log段添加、下面顯示頁面日志 array( 'class'=>'CWebLogRoute', 'levels'=>'trace', //級別為trace 'categories'=>'system.db.*' //只顯示關于數據庫信息,包括數據庫連接,數據庫執行語句 ),

完整如下:

  1. 'log'=>array
  2.  
  3.     'class'=>'CLogRouter'
  4.  
  5.     'routes'=>array
  6.  
  7.       array
  8.  
  9.         'class'=>'CFileLogRoute'
  10.  
  11.         'levels'=>'error, warning'
  12.  
  13.   
  14.  
  15.       ), 
  16.  
  17.               // 下面顯示頁面日志  
  18.  
  19.               array(  
  20.  
  21.                'class'=>'CWebLogRoute',  
  22.  
  23.                'levels'=>'trace',  //級別為trace  
  24.  
  25.                'categories'=>'system.db.*' //只顯示關于數據庫信息,包括數據庫連接,數據庫執行語句  
  26.  
  27.               ),  
  28.  
  29.       // uncomment the following to show log messages on web pages 
  30.  
  31.       /* 
  32.  
  33.       array( 
  34.  
  35.         'class'=>'CWebLogRoute', 
  36.  
  37.       ), 
  38.  
  39.       */ 
  40.  
  41.     ), 
  42.  
  43.   ), 

擴展 Yii2 自帶的日志組件:

  1. <?php 
  2.  
  3.   
  4.  
  5. /** 
  6.  
  7.  * author   : forecho <[email protected]> 
  8.  
  9.  * createTime : 2015/12/22 18:13 
  10.  
  11.  * description: 
  12.  
  13.  */ 
  14.  
  15. namespace common\components; 
  16.  
  17.   
  18.  
  19. use Yii; 
  20.  
  21. use yii\helpers\FileHelper; 
  22.  
  23.   
  24.  
  25. class FileTarget extends \yii\log\FileTarget 
  26.  
  27.  
  28.   /** 
  29.  
  30.    * @var bool 是否啟用日志前綴 (@app/runtime/logs/error/20151223_app.log) 
  31.  
  32.    */ 
  33.  
  34.   public $enableDatePrefix = false; 
  35.  
  36.   
  37.  
  38.   /** 
  39.  
  40.    * @var bool 啟用日志等級目錄 
  41.  
  42.    */ 
  43.  
  44.   public $enableCategoryDir = false; 
  45.  
  46.   
  47.  
  48.   private $_logFilePath = ''
  49.  
  50.   
  51.  
  52.   public function init() 
  53.  
  54.   { 
  55.  
  56.     if ($this->logFile === null) { 
  57.  
  58.       $this->logFile = Yii::$app->getRuntimePath() . '/logs/app.log'
  59.  
  60.     } else { 
  61.  
  62.       $this->logFile = Yii::getAlias($this->logFile); 
  63.  
  64.     } 
  65.  
  66.     $this->_logFilePath = dirname($this->logFile); 
  67.  
  68.   
  69.  
  70.     // 啟用日志前綴 
  71.  
  72.     if ($this->enableDatePrefix) { 
  73.  
  74.       $filename = basename($this->logFile); 
  75.  
  76.       $this->logFile = $this->_logFilePath . '/' . date('Ymd') . '_' . $filename
  77.  
  78.     } 
  79.  
  80.   
  81.  
  82.     if (!is_dir($this->_logFilePath)) { 
  83.  
  84.       FileHelper::createDirectory($this->_logFilePath, $this->dirMode, true); 
  85.  
  86.     } 
  87.  
  88.   
  89.  
  90.     if ($this->maxLogFiles < 1) { 
  91.  
  92.       $this->maxLogFiles = 1; 
  93.  
  94.     } 
  95.  
  96.     if ($this->maxFileSize < 1) { 
  97.  
  98.       $this->maxFileSize = 1; 
  99.  
  100.     } 
  101.  
  102.   
  103.  
  104.   } 
  105.  
  106.  
  107. </[email protected]

在配置文件中這樣使用:

  1. 'components' => [ 
  2.  
  3.   'log' => [ 
  4.  
  5.     'traceLevel' => YII_DEBUG ? 3 : 0, 
  6.  
  7.     'targets' => [ 
  8.  
  9.       /** 
  10.  
  11.        * 錯誤級別日志:當某些需要立馬解決的致命問題發生的時候,調用此方法記錄相關信息。 
  12.  
  13.        * 使用方法:Yii::error() 
  14.  
  15.        */ 
  16.  
  17.       [ 
  18.  
  19.         'class' => 'common\components\FileTarget'
  20.  
  21.         // 日志等級 
  22.  
  23.         'levels' => ['error'], 
  24.  
  25.         // 被收集記錄的額外數據 
  26.  
  27.         'logVars' => ['_GET''_POST''_FILES''_COOKIE''_SESSION','_SERVER'], 
  28.  
  29.         // 指定日志保存的文件名 
  30.  
  31.         'logFile' => '@app/runtime/logs/error/app.log'
  32.  
  33.         // 是否開啟日志 (@app/runtime/logs/error/20151223_app.log) 
  34.  
  35.         'enableDatePrefix' => true, 
  36.  
  37.         'maxFileSize' => 1024 * 1, 
  38.  
  39.         'maxLogFiles' => 100, 
  40.  
  41.       ], 
  42.  
  43.       /** 
  44.  
  45.        * 警告級別日志:當某些期望之外的事情發生的時候,使用該方法。 
  46.  
  47.        * 使用方法:Yii::warning() 
  48.  
  49.        */ 
  50.  
  51.       [ 
  52.  
  53.         'class' => 'common\components\FileTarget'
  54.  
  55.         // 日志等級 
  56.  
  57.         'levels' => ['warning'], 
  58.  
  59.         // 被收集記錄的額外數據 
  60.  
  61.         'logVars' => ['_GET''_POST''_FILES''_COOKIE''_SESSION','_SERVER'], 
  62.  
  63.         // 指定日志保存的文件名 
  64.  
  65.         'logFile' => '@app/runtime/logs/warning/app.log'
  66.  
  67.         // 是否開啟日志 (@app/runtime/logs/warning/20151223_app.log) 
  68.  
  69.         'enableDatePrefix' => true, 
  70.  
  71.         'maxFileSize' => 1024 * 1, 
  72.  
  73.         'maxLogFiles' => 100, 
  74.  
  75.       ], 
  76.  
  77.       /** 
  78.  
  79.        * info 級別日志:在某些位置記錄一些比較有用的信息的時候使用。 
  80.  
  81.        * 使用方法:Yii::info() 
  82.  
  83.        */ 
  84.  
  85.       [ 
  86.  
  87.         'class' => 'common\components\FileTarget'
  88.  
  89.         // 日志等級 
  90.  
  91.         'levels' => ['info'], 
  92.  
  93.         // 被收集記錄的額外數據 
  94.  
  95.         'logVars' => ['_GET''_POST''_FILES''_COOKIE''_SESSION','_SERVER'], 
  96.  
  97.         // 指定日志保存的文件名 
  98.  
  99.         'logFile' => '@app/runtime/logs/info/app.log'
  100.  
  101.         // 是否開啟日志 (@app/runtime/logs/info/20151223_app.log) 
  102.  
  103.         'enableDatePrefix' => true, 
  104.  
  105.         'maxFileSize' => 1024 * 1, 
  106.  
  107.         'maxLogFiles' => 100, 
  108.  
  109.       ], 
  110.  
  111.       /** 
  112.  
  113.        * trace 級別日志:記錄關于某段代碼運行的相關消息。主要是用于開發環境。 
  114.  
  115.        * 使用方法:Yii::trace() 
  116.  
  117.        */ 
  118.  
  119.       [ 
  120.  
  121.         'class' => 'common\components\FileTarget'
  122.  
  123.         // 日志等級 
  124.  
  125.         'levels' => ['trace'], 
  126.  
  127.         // 被收集記錄的額外數據 
  128.  
  129.         'logVars' => ['_GET''_POST''_FILES''_COOKIE''_SESSION','_SERVER'], 
  130.  
  131.         // 指定日志保存的文件名 
  132.  
  133.         'logFile' => '@app/runtime/logs/trace/app.log'
  134. //phpfensi.com 
  135.         // 是否開啟日志 (@app/runtime/logs/trace/20151223_app.log) 
  136.  
  137.         'enableDatePrefix' => true, 
  138.  
  139.         'maxFileSize' => 1024 * 1, 
  140.  
  141.         'maxLogFiles' => 100, 
  142.  
  143.       ], 
  144.  
  145.     ], 
  146.  
  147.   ], 
  148.  
  149. ], 

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()中當日志消息量過多時,及時將日志刷新寫入文件。代碼如下: 

  1. /** 
  2.  
  3.  * Initializes this application component. 
  4.  
  5.  * This method is required by the IApplicationComponent interface.   
  6.  
  7. */ 
  8.  
  9.  public function init(){  
  10.  
  11.   parent::init();  
  12.  
  13.   foreach($this->_routes as $name=>$route) {  
  14.  
  15.     $route=Yii::createComponent($route);   
  16.  
  17.     $route->init();   
  18.  
  19.     $this->_routes[$name]=$route;  
  20.  
  21.   }  
  22.  
  23.   Yii::getLogger()->attachEventHandler('onFlush',array($this,'collectLogs'));  
  24.  
  25.   Yii::app()->attachEventHandler('onEndRequest',array($this,'processLogs'));} 

而在CApplication::run()方法中定義了:

  1. if($this->hasEventHandler('onEndRequest')) { 
  2.  
  3. $this->onEndRequest(new CEvent($this)); 
  4.  

到這里我們可以理解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中的配置:

  1. 'preload'=>array('log'), 
  2.  
  3. components => array
  4.  
  5.        'log'=>array
  6.  
  7.          'class'=>'CLogRouter'
  8.  
  9.          'routes'=>array
  10.  
  11.           array
  12.  
  13.             'class'=>'CFileLogRoute'
  14.  
  15.             'levels'=>'error, warning,trace'
  16.  
  17.           ), 
  18.  
  19.          ) 
  20.  
  21.         ) 
  22.  
  23.        ) 

定義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事件處理句柄:

  1. if($this->hasEventHandler('onEndRequest')) { 
  2.  
  3.  $this->onEndRequest(new CEvent($this)); 
  4.  

也就是說,日志的最終處理(比如寫入文件,系統日志,發送郵件)是發生在應用程序運行完畢之后的。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組件添加新的路由配置:

  1. 'log'=>array
  2.  
  3. 'class'=>'CLogRouter'
  4.  
  5. 'routes'=>array
  6.  
  7. array
  8.  
  9. 'class'=>'CFileLogRoute'
  10.  
  11. 'levels'=>'error, warning,trace'
  12.  
  13. ), 
  14.  
  15. array
  16.  
  17. 'class' => 'myEmailLogRoute'
  18.  
  19. 'levels' => 'error', #所有異常的錯誤級別均為error,  
  20.  
  21. 'categories' => 'exception.CDbException', #數據庫產生錯誤時,均會產生CDbException異常。 
  22.  
  23. 'host' => 'mail.163.com'
  24.  
  25. 'port' => 25, 
  26.  
  27. 'user' => 'jeff_yu'
  28.  
  29. 'password' => 'you password'
  30.  
  31. 'timeout' => 30, 
  32.  
  33. 'emails' => '[email protected]', #日志接收人。 
  34.  
  35. 'sentFrom' => '[email protected]'
  36.  
  37. ), 

經過以上處理,即可使之實現我們的目的,當然你可以根據自己的需要進一步擴展之。

Tags: PHP Yii框架 日志功能

分享到:

福利彩票25选5开奖结果