zoukankan      html  css  js  c++  java
  • Yii2 log分析

    体验yii2的log功能

    1. 创建一个yii2的应用
    2. 在@app/Controllers目录新建一个TestController, 在控制器里新建一个actionIndex方法

      class TestController extends Controller{
       public function actionIndex() {
      
       }
      }
      

      注:@app是yii2中的路径别名语法,表示应用的基础目录

    3. 在actionIndex方法中写日志

      ...
      public function actionIndex() {
           //记录一个错误级别的日志
           Yii::error('人艰不拆!');
       }
      ...
      
    4. 进入yii2应用的日志目录@runtime/logs, 看到生成了一个名为app.log的文件, 内容如下:

       //日志产生时间  访问者ip地址 用户id SESSIONID 日志级别 日志类别  日志内容  
      1 2014-12-04 12:27:26 [10.18.60.111][-][-][error][application] 人艰不拆!
       //trace记录
      2     in /home/zhangjiulong/project/hulk_log/controllers/TestController.php:18
      

    yii2-log调用支持不同的报警级别

    1. 警告级别 WARNING

      public function actionIndex() {
           //记录一个警告级别的日志(日志内容可以为任何数据类型)
           Yii::warning(['source'=>'关电总局','content'=>'不能看美剧了']);
       }
      

      日志输入结果

      3 2014-12-04 14:23:32 [10.18.60.111][-][-][warning][application] [
      4     'source' => '关电总局',
      5     'content' => '不能看美剧了',
      6 ]
      7     in /home/zhangjiulong/project/hulk_log/controllers/TestController.php:18
      
    2. 提示级别 INFO

      //记录一个提示级别的日志
      Yii::info('喝酸奶只舔盖,我就是这么任性!');
      
    3. 代码追踪级别 TRACE
      //记录一个trace级别的日志
      Yii::trace('我这个级别,一般是在开发环境使用');
      

      trace级别的日志,只会在dev开发环境下才会触发, 所以Yii::trace方法中做了如下判断

       ...
       public static function trace($message, $category = 'application')
       {
           if (YII_DEBUG) {    //只有开发环境(YII_DEBUG为true)才会触发
               static::getLogger()->log($message, Logger::LEVEL_TRACE, $category);
           }
       }
       ...
      

    yii2-log支持不同的日志类别(如果你想将不同类型的日志写入不同文件的中)

    比如你想把与db相关的日志都写入到sql.log文件中

    1. 打开应用的配置文件@app/config/web.php, 在log组件的targets属性数组中添加一个"target"对象

      ...
      'components' => [
           ...
           'log' => [
               'traceLevel' => YII_DEBUG ? 3 : 0,
               'targets' => [
                   [
                       'class' => 'yiilogFileTarget',
                       'levels' => ['error', 'warning','info','trace','profile'],
                       'logVars'=>[],
                       //除了except对应的分类之外,其他的都写入到
                       'except'=>['yiidb*','appmodels*']
                   ],
                   //在原配置的基础上,增加以下配置(新增一个target)
                    [
                       'class' => 'yiilogFileTarget',
                       'levels' => ['error', 'warning','info','trace','profile'],
                       'logVars'=>[],
                       //表示以yiidb或者appmodels开头的分类都会写入这个文件
                       'categories'=>['yiidb*','appmodels*'],
                       //表示写入到文件sql.log中
                       'logFile'=>'@runtime/logs/sql.log',
                   ],
               ],
           ],
           ...
       ],
      ...
      
    2. 调用的时候,只需要加上第二个参数,填写对应的分类即可

      public function actionIndex() {
           //日志的第二个参数就是分类category
           Yii::info('select * from table', 'yiidbQuery');
      }
      
    3. 进入@runtime/logs下,可以看到生成了一个sql.log的文件

      └── logs
       ├── app.log
       └── sql.log
      //sql.log内容如下
      2014-12-05 18:58:51 [10.18.60.111][-][-][info][yiidbQuery] select * from table
      2     in /home/zhangjiulong/project/hulk_log/controllers/TestController.php:16
      

    yii2-log支持按时间来保存日志

    1. 打开应用的配置文件@app/config/web.php, 在log组件的target的logFile属性,日志文件名带上时间后缀即可
      ...
      'components' => [
           ...
           'log' => [
               'traceLevel' => YII_DEBUG ? 3 : 0,
               'targets' => [
                   [
                       'class' => 'yiilogFileTarget',
                       'levels' => ['error', 'warning','info','trace','profile'],
                       'logVars'=>[],
                       //表示以yiidb或者appmodels开头的分类都会写入这个文件
                       'categories'=>['yiidb*','appmodels*'],
                       //表示写入到文件sql.log.2014xxxx
                       'logFile'=>'@runtime/logs/sql.log.'.date('Ymd'),
                   ],
               ],
           ],
           ...
       ],
      ...
      
      2.再次使用Yii::info('select * from table', 'yiidbQuery');, 你会看到日志目录里
      logs
      ├── app.log
      ├── sql.log
      └── sql.log.20141208
      

    yii2-log支持将日志记录到数据库

    1. 需要先按下面的格式创建一个log表,日志名称可自定义,默认为log
    CREATE TABLE log (
        id       BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY,
        level    INTEGER,
        category VARCHAR(255),
        log_time INTEGER,
        prefix   TEXT,
        message  TEXT,
        INDEX idx_log_level (level),
        INDEX idx_log_category (category)
    )
    
    1. 打开应用的配置文件@app/config/web.php, 在log组件的targets中新增一个target----DbTarget
      ...
      'components' => [
           ...
           'log' => [
               'traceLevel' => YII_DEBUG ? 3 : 0,
               'targets' => [
                   ...
                   [
                       'class' => 'yiilogDbTarget',  //DbTaget类表示将日志记录到数据库中
                       'levels' => ['error', 'warning','info'],
                       'logVars'=>[],
                       'logTable'=>'log',//logTable表示要记录日志的表名,默认为log
                   ],
                   ...
               ],
           ],
           ...
       ],
      ...
      
    2. 此时当你再次使用Yii::info('切克闹');, 所以符合条件的target都会记录这条信息

      在表log中会插入一条数据

    +----+-------+-------------+------------+----------------------+-----------+
    | id | level | category    | log_time   | prefix               | message   |
    +----+-------+-------------+------------+----------------------+-----------+
    | 11 |     4 | application | 1418010724 | [10.18.60.111][-][-] | 切克闹 | 
    +----+-------+-------------+------------+----------------------+-----------+
    1 row in set (0.00 sec)
    

    在app.log文件中会追加一条数据

    2014-12-08 11:52:03 [10.18.60.111][-][-][info][application] 切克闹
    180     in /home/zhangjiulong/project/hulk_log/controllers/TestController.php:16
    

    yii2-log除了支持记录到文件和数据库,还可以直接发送邮件yiilogEmailTarget,记录到syslogyiilogSyslogTarget,此处不再细述, 更重要的是,你可以自定义target类来记录日志

    结构分析

    Yii2的日志主要由yiilogLogger,yiilogDispatcher,yiilogTarget三类来完成,其中Logger在内存中记录日志信息,当日志信息数达到一定量或者是脚本结束时, Logger就把日志信息交给了Dispatcher, Dispatcher把日志根据不同的配置分发到不同的Target子类,最终Target的子类来完成对日志的具体处理:

    yii2-log

    代码跟踪

    1.Yii类中yiidbLogger的实例化,,当调用Yii::info('反正我信了')时,Yii类(BaseYii)是如何反应的

    代码yiiBaseYii#353:
    //私有的静态属性$_logger用来储存Logger对象
    private static $_logger;
    //公有方法返回Logger的单例对象
        public static function getLogger()
        {
            if (self::$_logger !== null) {
                return self::$_logger;
            } else {
                return self::$_logger = static::createObject('yiilogLogger');
            }
        }
    //调用info等写日志方法时,实际调用的是Logger的log方法,并传递了写入的信息$message,日志等级,日志分类$category
     public static function info($message, $category = 'application')
        {
            static::getLogger()->log($message, Logger::LEVEL_INFO, $category);
        }
    

    2.yiilogLogger类的log方法

        代码yiilogLogger#135
        //将日志信息写入到$this->messages属性中
        public function log($message, $level, $category = 'application')
        {
            $time = microtime(true);
            $traces = [];
            //如果在应用初始化时配置log组件的traceLevel大于0,则将debug_backtrace信息也记录到日志中
            if ($this->traceLevel > 0) {
                $count = 0;
                $ts = debug_backtrace();
                array_pop($ts); // remove the last trace since it would be the entry script, not very useful
                foreach ($ts as $trace) {
                    if (isset($trace['file'], $trace['line']) && strpos($trace['file'], YII2_PATH) !== 0) {
                        unset($trace['object'], $trace['args']);
                        $traces[] = $trace;
                        if (++$count >= $this->traceLevel) {
                            break;
                        }
                    }
                }
            }
            //将信息保存到messages数组
            $this->messages[] = [$message, $level, $category, $time, $traces];
            //当保存的信息数量达到$this->flushInterval属性值时,执行flush方法清空日志,默认值为1000
            if ($this->flushInterval > 0 && count($this->messages) >= $this->flushInterval) {
                $this->flush();
            }
        }
        //清空日志信息,其实是调用yiidbDispatcher的dispatch方法,并传递信息数组$this->messages,和$final参数,$final为true表示脚本已执行结束,不管messages已经多少条都会写入
        public function flush($final = false)
        {
            if ($this->dispatcher instanceof Dispatcher) {
                $this->dispatcher->dispatch($this->messages, $final);
            }
            $this->messages = [];
        }
    

    3.yiilogDispatcher类的dispatch方法, 此类即应用的log组件

        代码yiilogDispatcher#177
        public $targets = [];    //targets来自于应用的配置,参见代码@app/configs/web.php
        public function dispatch($messages, $final)
        {
            $targetErrors = [];
            //遍历所有的targets,如果target可用(enabled属性为true),执行target的collect方法(collect方法在父类yiilogTarget中实现)
            foreach ($this->targets as $target) {
                if ($target->enabled) {
                    try {
                        //调用yiilogTarget的collect方法
                        $target->collect($messages, $final);
                    } catch (Exception $e) {
                        $target->enabled = false;
                        $targetErrors[] = [
                            'Unable to send log via ' . get_class($target) . ': ' . ErrorHandler::convertExceptionToString($e),
                            Logger::LEVEL_WARNING,
                            __METHOD__,
                            microtime(true),
                            [],
                        ];
                    }
                }
            }
    
            if (!empty($targetErrors)) {
                $this->dispatch($targetErrors, true);
            }
        }
    
    代码@app/configs/web.php
    ...
    'components' => [
            ...
            //此处配置yiilogDispatcher的属性
            'log' => [
                'traceLevel' => YII_DEBUG ? 3 : 0,    //traceLevel大于0,则会记录debug_backtrace信息
                //此处配置yiilogDispatcher的targets属性,每个子元素都是yiilogTarget的子类
                'targets' => [
                    [
                        'class' => 'yiilogDbTarget',  //DbTaget类表示将日志记录到数据库中
                        'levels' => ['error', 'warning','info'],
                        'logVars'=>[],
                        'logTable'=>'log',//logTable表示要记录日志的表名,默认为log
                    ],
                    ...
                ],
            ],
            ...
        ],
    ...
    

    4.yiilogTarget类的collect方法

        代码yiilogTarget#100
        //执行对$messages的收集
        public function collect($messages, $final)
        {
            //对$messages进行过滤,过滤掉不属于当前调用的target的信息
            $this->messages = array_merge($this->messages, $this->filterMessages($messages, $this->getLevels(), $this->categories, $this->except));
            $count = count($this->messages);
            //当日志信息数量达到执行数量$this->exportInterval时执行,默认值为1000
            if ($count > 0 && ($final || $this->exportInterval > 0 && $count >= $this->exportInterval)) {
                //记录$_GET,$_POST,$_SERVER等系统访问信息
                if (($context = $this->getContextMessage()) !== '') {
                    $this->messages[] = [$context, Logger::LEVEL_INFO, 'application', YII_BEGIN_TIME];
                }
                // set exportInterval to 0 to avoid triggering export again while exporting
                $oldExportInterval = $this->exportInterval;
                $this->exportInterval = 0;
                //调用export方法,此方法最终实现对message的处理,参见代码yiilogFileTarget
                $this->export();
                $this->exportInterval = $oldExportInterval;
    
                $this->messages = [];
            }
        }
    

    5.以日志写入到文件为例,yiilogFileTarget类的export方法

        代码yiilogFileTarget#97
        //将messages写入到文件$this->logFile中
        public function export()
        {
            //对messages进行格式化处理
            $text = implode("
    ", array_map([$this, 'formatMessage'], $this->messages)) . "
    ";
            if (($fp = @fopen($this->logFile, 'a')) === false) {
                throw new InvalidConfigException("Unable to append to log file: {$this->logFile}");
            }
            @flock($fp, LOCK_EX);
            // clear stat cache to ensure getting the real current file size and not a cached one
            // this may result in rotating twice when cached file size is used on subsequent calls
            clearstatcache();
            if (@filesize($this->logFile) > $this->maxFileSize * 1024) {
                $this->rotateFiles();
                @flock($fp, LOCK_UN);
                @fclose($fp);
                //执行写入
                @file_put_contents($this->logFile, $text, FILE_APPEND | LOCK_EX);
            } else {
                //执行写入
                @fwrite($fp, $text);
                @flock($fp, LOCK_UN);
                @fclose($fp);
            }
            if ($this->fileMode !== null) {
                @chmod($this->logFile, $this->fileMode);
            }
  • 相关阅读:
    37. Sudoku Solver(js)
    36. Valid Sudoku(js)
    35. Search Insert Position(js)
    34. Find First and Last Position of Element in Sorted Array(js)
    33. Search in Rotated Sorted Array(js)
    32. Longest Valid Parentheses(js)
    函数的柯里化
    俞敏洪:我和马云就差了8个字
    vue路由传值params和query的区别
    简述vuex的数据传递流程
  • 原文地址:https://www.cnblogs.com/wjq310/p/6375188.html
Copyright © 2011-2022 走看看