小白折騰伺服器(十一):postman 除錯 API 檢視當前介面全部 sql

aen233發表於2019-11-14

laravel-debugbar、Clockwork、Telescope.......
等等等等,telescope是laravel作者開發的,功能強大介面美觀沒得說,但是laravel版本勸退(5.7.7+)
Clockwork和laravel-debugbar都必須是瀏覽器中除錯,postman中就只能打log了....
有時候的sql又真的複雜,開發時檢視orm組裝的sql還是很有必要的,
打log有許多方法,但是我就是想看這一個介面的全部sql,我還不想去翻日誌

想了想就折騰了這段程式碼

思路是這樣的,

1.記日誌(當有指定引數時,往指定的檔案裡寫sql日誌)

給全域性加一個引數,dumpSql,當有dumpSql引數時,app/Providers/AppServiceProvider.php中的boot方法里加DB::listen往指定的檔案裡寫sql日誌

2.記日誌之前清空該指定檔案(保證每次記錄的都是當前請求介面的sql日誌)

這樣就保證每次請求的api中的sql資訊都是新的

3.響應中讀取該指定檔案,並將讀取的內容組成陣列,將該陣列放入響應中

這裡我用的是後置中介軟體,可以很方便的格式化響應資料,可以看這裡:小白折騰伺服器(七):自定義介面錯誤響應格式

思路很簡單,看程式碼吧~
app/Providers/AppServiceProvider.php中新增程式碼:

  public function boot()
    {
        $filename = storage_path('/logs/sql.log');
        if (file_exists($filename)) {
            unlink($filename);
        }
        // 只在本地開發環境啟用 SQL 日誌
        if (app()->environment(['local', 'dev']) && request('dumpSql', 0)) {
            DB::listen(function ($query) use ($filename) {
//                $sql = vsprintf(str_replace("?", "'%s'", $query->sql), $query->bindings);
                $sql = Str::replaceArray('?', $query->bindings, $query->sql);

                $sqlArr = [
                    'date' => (string)now(),
                    'sql'  => $sql,
                    'time' => $query->time,
                ];

                $logPath = storage_path('/logs/');
                if (!file_exists($logPath)) {
                    mkdir($logPath, 0777, true);
                }

                file_put_contents($filename, json_encode($sqlArr, 320) . PHP_EOL, FILE_APPEND);
            });
        }
    }

在後置中介軟體中加一個私有方法:
這個方法是按行讀取指定檔案,並將內容裝進陣列

   protected function readFileByLine($filename)
    {
        $fh = fopen($filename, 'r');

        $sql = [];
        while (!feof($fh)) {
            $sql[] = json_decode(fgets($fh), true);
        }

        fclose($fh);

        if (is_null(end($sql))) {
            array_pop($sql);
        }

        return $sql;
    }

修改後置中介軟體:
後置中介軟體格式化響應的時候,加上這3行程式碼,
當有dumpSql引數 && 存在指定sql日誌檔案時,拼接sql響應引數。

       $sqlFilename = storage_path('/logs/sql.log');
        if (request('dumpSql', 0) && file_exists($sqlFilename)) {
            $data['sql'] = $this->readFileByLine($sqlFilename);
        }

可以看下效果:

小白折騰伺服器(十一):給響應中加上sql資訊,postman除錯介面可以直接看sql啦

全部的後置中介軟體程式碼如下:

<?php

namespace App\Http\Middleware;

use Closure;
use Illuminate\Http\JsonResponse;
use Illuminate\Pagination\LengthAwarePaginator;
use Symfony\Component\HttpFoundation\BinaryFileResponse;

class After
{
    /**
     * 處理成功返回自定義格式
     *
     * @param \Illuminate\Http\Request $request
     * @param \Closure                 $next
     *
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $response = $next($request);

        if (is_array($response)) {
            return $response;
        }

        // 如果是匯出Excel型別直接返回
        if ($response instanceof BinaryFileResponse) {
            return $response;
        }

        // 執行動作
        $oriData = $response->getOriginalContent();
        $content = json_decode($response->getContent(), true) ?? $oriData;
        $content = is_array($oriData) ? $oriData : $content;

        if ($content['code'] ?? 0) {
            return $response;
        }

        $data['data'] = isset($content['data']) ? $content['data'] : $content;

        if ($content['meta'] ?? []) {
            $data['meta'] = [
                'total' => $content['meta']['total'],
                'page'  => $content['meta']['page'] ?? $content['meta']['current_page'] ?? 0,
                'size'  => $content['meta']['size'] ?? $content['meta']['per_page'] ?? 0,
            ];
        }

        if ($oriData instanceof LengthAwarePaginator) {
            $data['meta'] = [
                'total' => $content['total'],
                'page'  => $content['current_page'],
                'size'  => (int)$content['per_page'],
            ];
        }

        if ($data['data']['data'] ?? []) {
            $data['data'] = $data['data']['data'];
        }

        $sqlFilename = storage_path('/logs/sql.log');
        if (request('dumpSql', 0) && file_exists($sqlFilename)) {
            $data['sql'] = $this->readFileByLine($sqlFilename);
        }

        $message  = ['code' => 0, 'message' => 'success', 'data' => []];
        $temp     = ($content) ? array_merge($message, $data) : $message;
        $response = $response instanceof JsonResponse ? $response->setData($temp) : $response->setContent($temp);

        return $response;
    }

    /**
     * @param $filename
     *
     * @return array
     */
    protected function readFileByLine($filename)
    {
        $fh = fopen($filename, 'r');

        $sql = [];
        while (!feof($fh)) {
            $sql[] = json_decode(fgets($fh), true);
        }

        fclose($fh);

        if (is_null(end($sql))) {
            array_pop($sql);
        }

        return $sql;
    }
}

自己寫的小方法,最大的好處是自己熟悉,少依賴,如果用別人的包,可能一更新還得等作者更新,
當然最大的好處是比較方便啦^_^

一定一定要加 app()->environment(['local', 'dev']) 這個判斷呦

相關文章