给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;
    }
}
相关推荐
SilentSamsara23 分钟前
Python 环境搭建完整指南:从下载安装到运行第一个程序
开发语言·python
小短腿的代码世界36 分钟前
Qt文件系统与IO深度解析:从QFile到异步文件操作
开发语言·qt
合天网安实验室1 小时前
记录一个免杀的php webshell demo
渗透测试·php·webshell·免杀
AnalogElectronic1 小时前
linux 测试网络和端口是否连通的命令详解
linux·网络·php
harder3212 小时前
RMP模式的创新突破
开发语言·学习·ios·swift·策略模式
jinanwuhuaguo2 小时前
OpenClaw工程解剖——RAG、向量织构与“记忆宫殿”的索引拓扑学(第十三篇)
android·开发语言·人工智能·kotlin·拓扑学·openclaw
Rust研习社2 小时前
使用 Axum 构建高性能异步 Web 服务
开发语言·前端·网络·后端·http·rust
跨境数据猎手3 小时前
跨境独立站系统技术拆解(附带源码)
服务器·前端·php
淘矿人4 小时前
从0到1:用Claude启动你的第一个项目
开发语言·人工智能·git·python·github·php·pygame
cany10004 小时前
C++ -- 模板的声明和定义
开发语言·c++