zoukankan      html  css  js  c++  java
  • Yii2 Debug初探

    在初步理解了Yii2 Log的运行原理后,就可以通过yii2-debug的源码探索如下几个问题:

    (1)debug组件何时初始化?

    (2)页面底部的Debug工具条是在何时怎样渲染的?

    (3)Debug工具条进入不同板块的详情页时,各个板块的内容是从哪里来的?何时记录的?

    (4)Db板块如何实现对query的性能分析?

    1.Yii2 Debug简版类结构

    (1)yiidebugModule是Debug模块核心类,这里定义了Debug模块可以配置的属性信息以及模块初始化方法:

        1)访问控制信息$allowedIps与$allowedHosts;

        2)Debug模块包含的内容板块由corePanels()返回,板块属于yiidebugPanel类型,负责展示不同的日志信息;

        3)bootstrap()是Debug模块的初始化关键。Debug模块在配置文件中被添加到了yiiaseApplication的$bootstrap属性中,这里的模块或组件会在应用初始化时被实例化,同时由于Debug模块实现了BootstrapInterface接口,所以应用初始化时,Debug模块的bootstrap()方法就会被调用。这时主要做了三件事:

    第一:实例化Debug模块的$logTarget,并添加到Yii::$app->getLog()->targets['debug']中,这样便可以在日志分发组件(Yii::$app->getLog())分发日志消息时拿到系统记录的所有日志信息;

    第二:绑定渲染Debug工具条的事件处理函数,给View::EVENT_END_BODY事件追加一个处理方法renderToolbar(),这里渲染我们看到的页面底部的Debug工具条;给Response::EVENT_AFTER_PREPARE事件追加处理方法setDebugHeader();

    第三:将Debug模块关联的路由重写规则添加到UrlManager的规则中,以适应应用开启路由重写的场景。

        4)renderToolbar()会在View::EVENT_END_BODY事件触发时执行,将Debug工具条追加到视图页面的内容之后,到这里才知道页面底部的Debug工具条是何时渲染的。

    (2)yiidebugLogTarget是Debug模块的$logTarget成员变量所属类别,它与yiilogFileLog等具体Target一样均继承自yiilogTarget。这样一来,Debug的$logTarget就能够接收到log组件派发的所有日志消息,到这里才知道Debug模块显示的日志内容是从哪里来的。

        1)在yiidebugModule的bootstrap()方法中首先会实例化yiidebugLogTarget,这时会把当前yiidebugModule对象自身赋给yiidebugLogTarget的$module,把yiidebugModule的$tag值赋给yiidebugLogTarget的$tag;

        2)collect()方法将yiilogDispatcher(即log组件)派发的日志消息照单全收入$messages成员变量,并在特定的时机(即flush(true)调用时,这被yiilogLogger构造函数注册为shut down方法)调用export()方法将这些日志消息导出到特定目标;

        3)export()方法会将LogTarget记录的$messages导出到指定目录的debug日志文件,这一目录在yiidebugModule的$dataPath中指定。导出步骤主要有四步:

    第一:创建$tag值同名data文件;

    第二:整理要存入data文件的日志内容:首先整理summery内容,然后通过调用每个关联Panel的save()方法,整理出$module对象的所有板块对应的数据内容;

    第三:将第二步整理的日志内容序列化后存入第一步创建的data文件;

    第四:更新debug指定目录下的index.data文件内容,这一文件相当于所有{$tag}.data文件的索引文件,当需要查找某个$tag的data文件时都要先从索引文件中查找其是否存在。

    (3)yiidebugPanel是所有Debug内容板块的基类,由具体子类自行实现getName(),getSummary(),getDetail()等板块类别相关的方法。上图只列出了两个Panel子类作为示例。

        1)yiidebugModule的init()方法中初始化了相关板块initPanels();

        2)渲染页面底部工具条的具体任务在yiidebugcontrollersDefaultController的actionToolbar()中完成。首先根据$tag参数在index.data文件中检索,存在指定$tag则加载$tag同名data文件内容,由于data文件的内容是分板块索引存储的,所以可以方便地将加载的内容分发到对应的Panel对象(Panel::load()),以实现各个Panel的内容渲染;然后在工具条视图页面中逐个调用Panel对象的getSummary()方法渲染各自的summary视图内容,例如DbPanel在其getSummary()方法中渲染了此次请求的Db查询总次数与查询消耗总毫秒时间数。到这里才算知道工具条上一栏一栏的概括信息是怎么来的。

        3)每个板块的详情页内容渲染与上述流程大同小异,通过DefaultController的actionView()渲染详情页面,实际会调用指定Panel对象的getDetail()方法渲染各自的detail视图内容。

    (4)此时我比较感兴趣的是DbPanel的查询次数与查询时间的计算方式,其他板块的详细数据以后慢慢看。

        1)yiidebugpanelsDbPanel的getProfileLogs()过滤出了所有级别为Logger::LEVEL_PROFILE(0x40)且category为'yiidbCommand::query'或 'yiidbCommand::execute'的日志消息,将这些日志消息的内容除以2便是Db查询的总次数;

    原因:在Yii2 ActiveRecord的笔记中我们知道所有的sql语句的执行都由yiidbCommand的queryInternal()或execute()来执行,这里会在sql执行前后分别记录级别为Logger::LEVEL_PROFILE_BEGIN(0x50)或Logger::LEVEL_PROFILE_END(0x60),category为'yiidbCommand::query'或 'yiidbCommand::execute'的日志消息,而日志级别的过滤方式是与所需级别按位与运算,所以可以按上述方法来计算sql执行次数。

        2)sql执行总耗时的计算要从yiidebugpanelsDbPanel的calculateTimings()方法开始看起,它会调用yiilogLogger的calculateTimings()方法记录1)中过滤出的消息中每两个相邻的Logger::LEVEL_PROFILE_BEGIN与Logger::LEVEL_PROFILE_END日志的时间差,记为一次sql执行的耗时,再对所有耗时求和就是所有sql执行的总耗时。

  • 相关阅读:
    二十一.组合模式
    二十四.桥接模式
    二十六.职责链模式
    二十五.命令模式
    将小写转化成大写
    备份JOB SCHEDULE ENTRY的简单方法
    如何确定哪一个作业锁定QDLS下的一个目标
    WRKACTJOB命令一些有用功能介绍
    如何使用CA/400批处理的方式传输数据
    用前缀给字段命名
  • 原文地址:https://www.cnblogs.com/ling-diary/p/9210980.html
Copyright © 2011-2022 走看看