给ThinkPHP添加接口Trace

在日常开发中,有时候需要调试接口,没法使用Trace跟踪代码,今天根据官方的Trace改装了个可以在api接口里面获取Trace信息的中间,直接注册就可以使用;

php 复制代码
<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006~2019 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
declare(strict_types=1);

namespace app\common\middleware;

use Closure;
use think\App;
use think\Config;
use think\event\LogWrite;
use think\Request;
use think\Response;
use think\response\Redirect;

/**
 * 页面Trace中间件
 */
class JsonTraceDebug
{

    /**
     * Trace日志
     * @var array
     */
    protected $log = [];

    /**
     * 配置参数
     * @var array
     */
    // protected $config = [];

    protected $config = [
        'tabs' => ['base' => '基本', 'file' => '文件', 'info' => '流程', 'notice|error' => '错误', 'sql' => 'SQL', 'debug|log' => '调试'],
    ];

    /** @var App */
    protected $app;

    public function __construct(App $app, Config $config)
    {
        $this->app = $app;
    }

    /**
     * 页面Trace调试
     * @access public
     * @param Request $request
     * @param Closure $next
     * @return void
     */
    public function handle($request, Closure $next)
    {
        $debug = $this->app->isDebug();

        // 注册日志监听
        if ($debug) {
            $this->log = [];
            $this->app->event->listen(LogWrite::class, function ($event) {
                if (empty($this->config['channel']) || $this->config['channel'] == $event->channel) {
                    $this->log = array_merge_recursive($this->log, $event->log);
                }
            });
        }

        $response = $next($request);

        // Trace调试注入
        if ($debug) {
            $data = $response->getContent();
            if ($response instanceof Redirect) {
                //TODO 记录
            } else {
                $log = $this->app->log->getLog('');
                $log = array_merge_recursive($this->log, $log);
                $trace = $this->output($response, $log);
                $data = json_decode($data, true);
                $data['trace'] = $trace;
            }
            $response->content(json_encode($data));
        }

        return $response;
    }

    /**
     * 调试输出接口
     * @access public
     * @param  Response  $response Response对象
     * @param  array     $log 日志信息
     * @return string|bool
     */
    public function output(Response $response, array $log = [])
    {
        $request = $this->app->request;
        $contentType = $response->getHeader('Content-Type');
        if (!empty($contentType) && strpos($contentType, 'html') !== false) {
            return false;
        } elseif ($response->getCode() == 204) {
            return false;
        }

        // 获取基本信息
        $runtime = number_format(microtime(true) - $this->app->getBeginTime(), 10, '.', '');
        $reqs = $runtime > 0 ? number_format(1 / $runtime, 2) : '∞';
        $mem = number_format((memory_get_usage() - $this->app->getBeginMem()) / 1024, 2);

        if ($request->host()) {
            $uri = $request->protocol() . ' ' . $request->method() . ' : ' . $request->url(true);
        } else {
            $uri = 'cmd:' . implode(' ', $_SERVER['argv']);
        }

        // 页面Trace信息
        $base = [
            '请求信息' => date('Y-m-d H:i:s', $request->time() ?: time()) . ' ' . $uri,
            '运行时间' => number_format((float) $runtime, 6) . 's [ 吞吐率:' . $reqs . 'req/s ] 内存消耗:' . $mem . 'kb 文件加载:' . count(get_included_files()),
            '查询信息' => $this->app->db->getQueryTimes() . ' queries',
            '缓存信息' => $this->app->cache->getReadTimes() . ' reads,' . $this->app->cache->getWriteTimes() . ' writes',
        ];

        if (isset($this->app->session)) {
            $base['会话信息'] = 'SESSION_ID=' . $this->app->session->getId();
        }

        $info = $this->getFileInfo();

        // 页面Trace信息
        $trace = [];
        foreach ($this->config['tabs'] as $name => $title) {
            $name = strtolower($name);
            switch ($name) {
                case 'base': // 基本信息
                    $trace[$title] = $base;
                    break;
                case 'file': // 文件信息
                    $trace[$title] = $info;
                    break;
                default: // 调试信息
                    if (strpos($name, '|')) {
                        // 多组信息
                        $names = explode('|', $name);
                        $result = [];
                        foreach ($names as $item) {
                            $result = array_merge($result, $log[$item] ?? []);
                        }
                        $trace[$title] = $result;
                    } else {
                        $trace[$title] = $log[$name] ?? '';
                    }
            }
        }
        $trace['SQL'] = array_reverse($trace['SQL']);
        return $trace;
    }


    /**
     * 获取文件加载信息
     * @access protected
     * @return integer|array
     */
    protected function getFileInfo()
    {
        $files = get_included_files();
        $info = [];

        foreach ($files as $key => $file) {
            $info[] = $file . ' ( ' . number_format(filesize($file) / 1024, 2) . ' KB )';
        }

        return $info;
    }
}
相关推荐
isyangli_blog4 小时前
OpenDayLight (Carbon 版本) 启动与组件安装
开发语言·php
vb2008114 小时前
FastAPI APIRouter
开发语言·python
Benszen4 小时前
KVM虚拟化解决方案
开发语言·perl
会编程的土豆4 小时前
Go 语言反射(Reflection)详解
开发语言·后端·golang
東雪木4 小时前
多线程与并发编程 专属复习笔记
java·开发语言·笔记·java面试
杨充5 小时前
1.3 浮点型数据设计灵魂
开发语言·python·算法
噜噜噜阿鲁~5 小时前
python学习笔记 | 11.3、面向对象高级编程-多重继承
java·开发语言
basketball6165 小时前
Go 语言从入门到进阶:4. 数组和MAP使用方法总结
开发语言·后端·golang
春生野草5 小时前
反射、Tomcat执行
java·开发语言
雪的季节6 小时前
企业级 Qt 全功能项目
开发语言·数据库·qt