DEV Community

Cover image for ต่อยอด Laravel Debugbar เพื่อดีบัก Rest API Routes
Atthaphon Urairat
Atthaphon Urairat

Posted on

ต่อยอด Laravel Debugbar เพื่อดีบัก Rest API Routes

ปัญหา 👹

บทความนี้เขียนจากประสบการณ์จริง โปรเจ็คที่ผมทำอยู่มีปัญหาด้านความเร็วในการตอบสนอง เพราะ endpoint บางตัวใช้เวลาในการตอบสนองนานกว่า 10s ทำให้ไม่สามารถที่จะยอมรับได้ เพราะ ช้าเกินไปมากๆๆๆ 😩😩😩 ถ้าเป็นคนที่ทำโปรเจ็คมาตั้งแต่ต้น การดีบักก็คงไม่มีปัญหา

ผมลองจินตนาการดู ถ้าเราไม่ได้ทำโปรเจ็คต่อล่ะ สมมุติถ้าคนที่มาจับโปรเจ็คต่อไม่ได้มีงานแค่ตัวเดียวล่ะ เขาต้องมาเสียเวลาส่วนมากไปกับแก้ปัญหานิดๆ หน่อยๆ อย่างนั้นหรือ ดูไม่คุ้มเลย 😡

ผมเลย install package Debugbar เผื่อไว้เพื่อทีจะเอาไว้ดีบัก แต่ติดที่ว่า debugbar ไม่สามารถโชว์ผลลัพธ์ใน api route ได้ 😭😭 เอาล่ะสิต้องพึ่งพา อากู๋ หน่อยล่ะ แล้วในที่สุดหลังจากการ research ผมก็ได้มาซึ่งสิ่งที่ต้องการ 🎉🎉🎉

Framework & Packages ⚙️

# install laravel 5.4
composer create-project --prefer-dist laravel/laravel yourProjectName "5.4.*"
# install debugbar 2.4 that compatible with laravel 5.4
composer require barryvdh/laravel-debugbar:~2.4
# install dingo/api
composer require dingo/api

ผมไม่ได้แสดงวิธี install ทุกขั้นตอนนะครับ เพื่อนๆ สามารถอ่านวิธีการ install และ config เบื้องต้นได้จาก link ที่ให้ไว้ได้นะครับ

วิธีทำ 👩‍🏫

จากที่เห็น โปรเจ็คตัวนี้ใช้ laravel v5.4, debugbar v2.4 และ dingo api v1 ส่ิงที่เราต้องการคือ การโชว์ข้อมูลจาก debugbar เมื่อเราทำการ access api route จาก browser หรือ postman

เริ่มด้วยการสร้าง Middleware

<?php

namespace App\Http\Middleware;

use Closure;
use Dingo\Api\Http\Response;

class ProfileDingoHttpResponse
{
    /**
     * Handle an incoming request.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  \Closure  $next
     * @return mixed
     */
    public function handle($request, Closure $next)
    {
        $response = $next($request);

        if ($response instanceof Response &&
            app()->bound('debugbar') &&
            app('debugbar')->isEnabled()
        ) {
            $queries_data = $this->sqlFilter(app('debugbar')->getData());
            $response->setContent(json_decode($response->morph()->getContent(), true) + [
                '_debugbar' => [
                    'total_queries' => count($queries_data),
                    'queries' => $queries_data,
                    'time' => app('debugbar')->getData()['time'],
                    'memory' => app('debugbar')->getData()['memory'],
                ],
            ]);
        }

        return $response;
    }

    /**
     * Get only sql and each duration
     *
     * @param $debugbar_data
     * @return array
     */
    protected function sqlFilter($debugbar_data)
    {
        $result = array_get($debugbar_data, 'queries.statements');

        return array_map(function ($item) {
            return [
                'sql' => array_get($item, 'sql'),
                'duration' => array_get($item, 'duration_str'),
            ];
        }, $result);
    }
}

จากนั้นทำการเพิ่ม middleware ProfileDingoHttpResponse ที่เราเพิ่งสร้างไป ในไฟล์ Kernel.php ซึ่งอยู่ใน yourProjectName/app/Http/Kernel.php

<?php

namespace App\Http;

use Illuminate\Foundation\Http\Kernel as HttpKernel;

class Kernel extends HttpKernel
{
    ...

    protected $routeMiddleware = [
        ...
        'profile-dingo-http' => \App\Http\Middleware\ProfileDingoHttpResponse::class,
    ];
}

หลังจากที่เราเพิ่ม 'profile-dingo-http' => \App\Http\Middleware\ProfileDingoHttpResponse::class, ไปใน $routeMiddleware จากนั้นเราก็สามารถเพิ่ม middleware ได้แล้ว โดยในโปรเจ็คนี้เราใช้ Dingo api ซึ่งจะเห็นวิธีเพิ่ม middleware ได้จากตัวอย่างด้านล่าง

<?php
$api = app('Dingo\Api\Routing\Router');

$api->version('v1', ['middleware' => 'profile-dingo-http'], function ($api) {
    ...
});

🎉🎉🎉 เสร็จแล้วครับ

ผลลัพธ์ 💯

หลังจากทำการเรียก api จาก url เราจะได้ผลลัพธ์ซึ่งเป็นข้อมูลจาก debugbar ที่เราสร้างไว้ใน Middleware ProfileDingoHttpResponse ห้อยท้ายมาด้วย

นี่คือผลลัพธ์ที่ได้ห้อยท้ายเพิ่มเติมเมื่อเราเพิ่ม middleware เข้าไปครับ ถ้าหากไม่ชัดก็สามารถดูตัวอย่างจากด้านล่างได้ครับ

"_debugbar": {
    "total_queries": 1,
    "queries": [
        {
            "sql": "select `id`, `name`, `description` from `product` where `id` = '2' limit 1",
            "duration": "14.01ms"
        }
    ],
    "time": {
        "start": 1561688591.7021,
        "end": 1561688591.8966,
        "duration": 0.19451189041138,
        "duration_str": "194.51ms",
        "measures": [
            {
                "label": "Booting",
                "start": 1561688591.7021,
                "relative_start": 0,
                "end": 1561688591.786,
                "relative_end": 1561688591.786,
                "duration": 0.083874940872192,
                "duration_str": "83.87ms",
                "params": [],
                "collector": null
            },
            {
                "label": "Application",
                "start": 1561688591.7738,
                "relative_start": 0.071721792221069,
                "end": 1561688591.8966,
                "relative_end": 0.0000050067901611328,
                "duration": 0.12279510498047,
                "duration_str": "122.8ms",
                "params": [],
                "collector": null
            }
        ]
    },
    "memory": {
        "peak_usage": 19660800,
        "peak_usage_str": "18.75MB"
    }
}

ด้วยเหตุฉนั้น เลยได้ผลลัพธ์ในแบบฉนี้ และแล้วในตอนนี้การ debug ก็จะง่ายขึ้นไม่ว่าจะสำหรับคนเก่าหรือใหม่ ซึ่งสามารถทำให้เราหาสาเหตุของสิ่งที่เราต้องการแก้หรือปรับปรุงได้ง่ายยิ่งขึ้น ขอบคุณครับ จบแล้วครับ 🥳 🥳 🥳

Source & so much thanks ❤️🎉🥰

REST API profiling #252

Discussion (0)