欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

 更新時間:2016年03月17日 14:24:29   作者:柏樹_Jeff  
這篇文章主要介紹了PHP的Yii框架中的日志,對日志的分析是日常網(wǎng)站維護(hù)中的基礎(chǔ),Yii提供了較為強(qiáng)大的日志功能,需要的朋友可以參考下

Yii頁面級日志開啟
在 Main.php中 log段添加、
下面顯示頁面日志 array( 'class'=>'CWebLogRoute', 'levels'=>'trace', //級別為trace 'categories'=>'system.db.*' //只顯示關(guān)于數(shù)據(jù)庫信息,包括數(shù)據(jù)庫連接,數(shù)據(jù)庫執(zhí)行語句 ),
完整如下:

'log'=>array(
    'class'=>'CLogRouter',
    'routes'=>array(
      array(
        'class'=>'CFileLogRoute',
        'levels'=>'error, warning',

      ),
              // 下面顯示頁面日志 
              array( 
               'class'=>'CWebLogRoute', 
               'levels'=>'trace',  //級別為trace 
               'categories'=>'system.db.*' //只顯示關(guān)于數(shù)據(jù)庫信息,包括數(shù)據(jù)庫連接,數(shù)據(jù)庫執(zhí)行語句 
              ), 
      // uncomment the following to show log messages on web pages
      /*
      array(
        'class'=>'CWebLogRoute',
      ),
      */
    ),
  ),

    
擴(kuò)展 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' => [
      /**
       * 錯誤級別日志:當(dāng)某些需要立馬解決的致命問題發(fā)生的時候,調(diào)用此方法記錄相關(guān)信息。
       * 使用方法:Yii::error()
       */
      [
        'class' => 'common\components\FileTarget',
        // 日志等級
        'levels' => ['error'],
        // 被收集記錄的額外數(shù)據(jù)
        '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,
      ],
      /**
       * 警告級別日志:當(dāng)某些期望之外的事情發(fā)生的時候,使用該方法。
       * 使用方法:Yii::warning()
       */
      [
        'class' => 'common\components\FileTarget',
        // 日志等級
        'levels' => ['warning'],
        // 被收集記錄的額外數(shù)據(jù)
        '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'],
        // 被收集記錄的額外數(shù)據(jù)
        '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 級別日志:記錄關(guān)于某段代碼運(yùn)行的相關(guān)消息。主要是用于開發(fā)環(huán)境。
       * 使用方法:Yii::trace()
       */
      [
        'class' => 'common\components\FileTarget',
        // 日志等級
        'levels' => ['trace'],
        // 被收集記錄的額外數(shù)據(jù)
        '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使用層次的日志處理機(jī)制,即日志的收集與日志最終的處理(如顯示、保存到文件、保存到數(shù)據(jù)數(shù))是分離的。
日志信息的收集由CLogger(日志記錄器)完成,而日志信息的分發(fā)處理,則在CLogRouter的調(diào)度(稱為日志路由管理器)下,分發(fā)給處理對象(如CFileLogRoute以及l(fā)ogging目錄下繼承自CLogRoute的類, 稱為日志處理器),經(jīng)過反復(fù)閱讀其源代碼,我更是為Yii的設(shè)計思想所折服,如此的分層處理,使得其易于靈活擴(kuò)展。
而日志信息有級別之分,如普通的info, profile, trace, warning, error級別,可以在日志路由中設(shè)置過慮條件,如設(shè)置CFileRoute的levels屬性,即可只處理指定級別的日志信息。
如在程序中調(diào)用:

Yii::log($message,CLogger::LEVEL_ERROR,$category);

對應(yīng)的流程可能如下:

  • 生成CLogger實例
  • 如果YII_DEBUG , YII_TRACE_LEVEL都已經(jīng)定義為有效值,并且日志級別不是profile, 則產(chǎn)生調(diào)用回溯信息, 并追加到日志信息上。
  • 調(diào)用CLogger:: log($msg,$level,$category) 收集日志,實際上這時日志并沒有寫入文件,僅僅是暫存于內(nèi)存之中。

問題:日志是在何時被寫入文件的?
經(jīng)過反復(fù)跟蹤,我發(fā)現(xiàn)在CLogRouter類的init方法中為Application對象的OnEndRequest事件綁定處理器CLogRouter::processLogs()。同時也給Yii::$_logger的onFlush事件綁定事件處理器CLogRouter::collectLogs方法,用于在Yii::log()中當(dāng)日志消息量過多時,及時將日志刷新寫入文件。代碼如下:

/**
 * 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)僅僅是將日志進(jìn)行收集(記錄到內(nèi)容結(jié)構(gòu)之中),然后在程序結(jié)束時,由$app對象調(diào)用CLogRouter的processLogs進(jìn)行日志的處理。Yii支持日志多道路由,比如:同一份日志即可寫入至文件,又可顯示到頁面上,甚至同時以電子郵件發(fā)送,更甚至同時記錄到數(shù)據(jù)庫中,這是由配置文件中的log:routes配置實現(xiàn)的,為log:routes配置多個元素,實現(xiàn)多個路由分發(fā)。日志信息的過濾,記錄均是由最終的日志處理器處理。
日志處理器要完成的任務(wù)主要包含以下幾點(diǎn): 從CLogger中取得所有日志,并進(jìn)行過濾(主要是levels, categories兩項定義log:routes:levels/categories)

先進(jìn)行過濾參考CFileLogRoute::collectLogs()中的邏輯:

 $logs=$logger->getLogs($this->levels,$this->categories); //執(zhí)行過濾,只得到期望信息

日志過濾已經(jīng)完成接下來就要對日志進(jìn)行最終處理(如寫入到文件,記錄至數(shù)據(jù)庫等)

 CFileLogRoute::processLogs($logs);

但這個函數(shù)之中,有個小bug, 只判斷日志目錄是否可寫,沒有判斷日志文件本身是否可寫.CFileLogRoute實現(xiàn)了類似Linux的日志輪換功能(LogRoate), 并規(guī)定了日志文件的大小,考慮得很周到,很完善! 我也要向其學(xué)習(xí)并吸收其思想!
protected/config/main.php中的配置:

'preload'=>array('log'),
components => array(
       'log'=>array(
         'class'=>'CLogRouter',
         'routes'=>array(
          array(
            'class'=>'CFileLogRoute',
            'levels'=>'error, warning,trace',
          ),
         )
        )
       )

定義log組件需要預(yù)先加載(實例化)。配置使用CLogRouter作為日志路由管理器,并設(shè)置了其日志路由處理器(routes屬性)及其配置屬性。而preload, log屬性的定義,均要應(yīng)用到CWebApplication對象上(請參閱CApplication::__construct中的configure調(diào)用, configure從CModule繼承而來)。而在CWebApplication的構(gòu)造函數(shù)中執(zhí)行preloadComponents(),就創(chuàng)建了log對象(即CLogRouter的實例)。
創(chuàng)建并初始化一個組件時,實際上調(diào)用的是CModule::getComponent, 這個調(diào)用中使用YiiBase::createComponent創(chuàng)建組件對象,并再調(diào)用組件的init初始化之。
再閱讀CLogRouter::init()過程,在這里有兩個關(guān)鍵之處,一是創(chuàng)建日志路由處理器(即決定日志的最終處理方式:寫入文件,郵件發(fā)送等等),二是給應(yīng)用程序?qū)ο蠼壎╫nEndRequest事件處理CLogRouter::processLogs()。而在CApplication::run()確實有相關(guān)代碼用于運(yùn)行onEndRequest事件處理句柄:

 if($this->hasEventHandler('onEndRequest')) {
  $this->onEndRequest(new CEvent($this));
 }

也就是說,日志的最終處理(比如寫入文件,系統(tǒng)日志,發(fā)送郵件)是發(fā)生在應(yīng)用程序運(yùn)行完畢之后的。Yii使用事件機(jī)制,巧妙地實現(xiàn)了事件與處理句柄的關(guān)聯(lián)。
也就是說,當(dāng)應(yīng)用程序運(yùn)行完畢,將執(zhí)行CLogRouter::processLogs,對日志進(jìn)行處理,。CLogRouter被稱之為日志路由管理器。每個日志路由處理器從CLooger對象中取得相應(yīng)的日志(使用過濾機(jī)制),作最終處理。
具體而言Yii的日志系統(tǒng),分為以下幾個層次:

日志發(fā)送者,即程序中調(diào)用Yii::log($msg, $level, $category),將日志發(fā)送給CLogger對象
CLogger對象負(fù)責(zé)將日志記錄暫存于內(nèi)存之中程序運(yùn)行結(jié)束后,log組件(日志路由管理器CLogRoute)的processLogs方法被激活執(zhí)行,由其逐個調(diào)用日志路由器,作日志的最后處理。

更為詳細(xì)的大致過程如下:

  • CApplication::__construct()中調(diào)用preloadComponents, 這導(dǎo)致log組件(CLogRoute)被實例化,并被調(diào)用init方法初始化。
  • log組件(CLogRoute)的init方法中,其是初始化日志路由,并給CApplication對象onEndRequest事件綁定處理流程processLogs。給CLooger組件的onFlush事件綁定處理流程collectLogs。
  • 應(yīng)用程序的其它部分通過調(diào)用Yii::log()向CLogger組件發(fā)送日志信息,CLogger組件將日志信息暫存到內(nèi)存中。
  • CApplication執(zhí)行完畢(run方法中),會激活onEndRequest事件,綁定的事件處理器processLogs被執(zhí)行,日志被寫入文件之中。 Yii的日志路由機(jī)制,給日志系統(tǒng)擴(kuò)展帶來了無限的靈活。并且其多道路由處理機(jī)制,可將同一份日志信息進(jìn)行多種方式處理。

這里舉出一個案例:發(fā)生error級別的數(shù)據(jù)庫錯誤時,及時給相關(guān)維護(hù)人員發(fā)送電子郵件,并同時將這些日志記錄到文件之中。規(guī)劃思路,發(fā)送郵件和手機(jī)短信是兩個不同的功能,Yii已經(jīng)帶了日志郵件發(fā)送組件(logging/CEmailLogRoute.php),但這個組件中卻使用了php自帶的mail函數(shù),使用mail函數(shù)需要配置php.ini中的smtp主機(jī),并且使用非驗證發(fā)送方式,這種方式在目前的實際情況下已經(jīng)完全不可使用。代替地我們需要使用帶驗證功能的smtp發(fā)送方式。在protected/components/目錄下定義日志處理器類myEmailLogRoute,并讓其繼承自CEmailLogRoute,最主要的目的是重寫CEmailLogRoute::sendEmail()方法  ,其中,SMTP的處理細(xì)節(jié)請自行完善(本文的重點(diǎn)是放在如何處理日志上,而不是發(fā)送郵件上)。
接下來,我們就可以定義日志路由處理,編輯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', #數(shù)據(jù)庫產(chǎn)生錯誤時,均會產(chǎn)生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',
),

經(jīng)過以上處理,即可使之實現(xiàn)我們的目的,當(dāng)然你可以根據(jù)自己的需要進(jìn)一步擴(kuò)展之。

相關(guān)文章

最新評論