侯体宗的博客
  • 首页
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

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

php  /  管理员 发布于 7年前   276

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 自带的日志组件
   

  * 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' => '[email protected]', #日志接收人。'sentFrom' => '[email protected]',),

经过以上处理,即可使之实现我们的目的,当然你可以根据自己的需要进一步扩展之。

您可能感兴趣的文章:

  • PHP的Yii框架的常用日志操作总结
  • 详解PHP的Yii框架中日志的相关配置及使用
  • Yii2框架中日志的使用方法分析
  • YII Framework框架教程之日志用法详解
  • Yii框架实现记录日志到自定义文件的方法
  • Yii框架日志记录Logging操作示例
  • Yii配置文件用法详解
  • YII2 实现多语言配置的方法分享
  • yii2 数据库读写分离配置示例
  • Yii框架日志操作图文与实例详解


  • 上一条:
    深入解析PHP的Yii框架中的event事件机制
    下一条:
    关于PHP中Session文件过多的问题及session文件保存位置
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • PHP 8.4 Alpha 1现已发布!(0个评论)
    • 用Time Warden监控PHP中的代码处理时间(0个评论)
    • 在PHP中使用array_pop + yield实现读取超大型目录功能示例(0个评论)
    • Property Hooks RFC在PHP 8.4中越来越接近现实(0个评论)
    • 近期文章
    • 在windows10中升级go版本至1.24后LiteIDE的Ctrl+左击无法跳转问题解决方案(0个评论)
    • 智能合约Solidity学习CryptoZombie第四课:僵尸作战系统(0个评论)
    • 智能合约Solidity学习CryptoZombie第三课:组建僵尸军队(高级Solidity理论)(0个评论)
    • 智能合约Solidity学习CryptoZombie第二课:让你的僵尸猎食(0个评论)
    • 智能合约Solidity学习CryptoZombie第一课:生成一只你的僵尸(0个评论)
    • 在go中实现一个常用的先进先出的缓存淘汰算法示例代码(0个评论)
    • 在go+gin中使用"github.com/skip2/go-qrcode"实现url转二维码功能(0个评论)
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(95个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2016-11
    • 2017-06
    • 2017-07
    • 2017-08
    • 2017-09
    • 2017-11
    • 2017-12
    • 2018-01
    • 2018-02
    • 2018-03
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2020-07
    • 2020-09
    • 2021-02
    • 2021-03
    • 2021-04
    • 2021-05
    • 2021-06
    • 2021-07
    • 2021-08
    • 2021-09
    • 2021-10
    • 2021-11
    • 2021-12
    • 2022-01
    • 2022-02
    • 2022-05
    • 2022-06
    • 2022-07
    • 2022-08
    • 2022-09
    • 2022-10
    • 2022-11
    • 2022-12
    • 2023-01
    • 2023-02
    • 2023-03
    • 2023-04
    • 2023-05
    • 2023-06
    • 2023-07
    • 2023-08
    • 2023-09
    • 2023-10
    • 2023-11
    • 2023-12
    • 2024-01
    • 2024-02
    • 2024-03
    • 2024-04
    • 2024-05
    • 2024-06
    • 2024-07
    • 2024-09
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客