Pulse เปิดตัวที่งาน Laracon AU เมื่อ 16 พ.ย. 2566 เป็น open source ใช้ได้โดยไม่มีค่าใช้จ่าย ตัว dashboard เขียนด้วย Livewire ตอนนี้ออกถึง v1.8.1 แล้ว รองรับ Laravel 10 ถึง 13 ใช้คู่กับ Livewire 3 หรือ 4 ก็ได้ เว็บนี้เองผมก็ติดตั้ง Pulse ไว้ดูว่าหน้าไหนเริ่มช้า

Pulse บนเวที Laracon AU 2023 ในหน้าเดียวมีทั้ง CPU และ memory ของ server, Application Usage, Queues, Cache และ Slow Queries
ติดตั้ง Laravel Pulse
Pulse เก็บข้อมูลลง database ซึ่งเอกสารของ Laravelระบุว่าต้องเป็น MySQL, MariaDB หรือ PostgreSQL ติดตั้งผ่าน Composer แล้ว publish ไฟล์ config กับ migration จากนั้นสั่ง migrate เพื่อสร้างตาราง pulse_*
composer require laravel/pulse
php artisan vendor:publish --provider="Laravel\Pulse\PulseServiceProvider"
php artisan migrate
เสร็จแล้วเปิด /pulse บนเครื่อง local ได้เลย ค่าที่ปรับได้อยู่ใน config/pulse.php และเกือบทุกค่าตั้งผ่าน .env ได้ เช่น PULSE_PATH ถ้าอยากย้าย dashboard ไปไว้ที่อื่น
เปิดหน้า /pulse บน production ด้วย gate viewPulse
ค่าเริ่มต้น Pulse ให้เข้า /pulse ได้เฉพาะตอน APP_ENV=local พอขึ้น server จริงจะได้ 403 ทุกคน เราต้องกำหนด gate viewPulse เองว่าใครเข้าได้ ใส่ไว้ใน AppServiceProvider
use App\Models\User;
use Illuminate\Support\Facades\Gate;
public function boot(): void
{
Gate::define('viewPulse', function (User $user) {
return in_array($user->email, [
'[email protected]',
]);
});
}
closure รับ User แบบไม่ nullable คนที่ยังไม่ login จึงเข้าไม่ได้ ถ้าแอปมี role อยู่แล้วก็เปลี่ยนไปเช็ก role แทนรายชื่ออีเมลได้
Pulse มีการ์ดอะไรบ้าง
หน้า dashboard ตั้งต้นมาเก้าการ์ด แต่ละการ์ดดึงข้อมูลจาก recorder ของตัวเองใน config/pulse.php เราปิด recorder ที่ไม่ใช้ หรือปรับ threshold ทีละตัวได้
Slow Requests บอกว่า route ไหนช้า
การ์ดนี้แสดง route ที่ตอบช้ากว่า threshold (ค่าเริ่มต้น 1,000 ms) พร้อม method, URL และ action ของ controller เรียงตามจำนวนครั้งหรือตามเวลาที่ช้าสุด ตอนเปิดตัวการ์ดนี้ชื่อ Slow Routes อย่างในภาพ

แต่ละแถวบอก method, route และ controller action ที่ตอบช้าเกิน threshold
บาง route ช้าโดยธรรมชาติ เช่น หน้าออกรายงาน เราตั้ง threshold แยกเป็นราย route ได้ด้วย regex
Recorders\SlowRequests::class => [
// ...
'threshold' => [
'#^/admin/reports#' => 5000,
'default' => env('PULSE_SLOW_REQUESTS_THRESHOLD', 1000),
],
],
Slow Queries บอกว่า query ไหนช้า และอยู่บรรทัดไหน
Pulse เก็บ SQL ที่ช้ากว่า threshold โดยไม่ใส่ค่า binding แล้วจัดกลุ่มตาม SQL กับไฟล์และบรรทัดที่เรียก เราเลยรู้ทันทีว่าต้องไปแก้ที่ไหน ถ้าเจอ query เดิมซ้ำเป็นร้อยครั้งใน request เดียว ลองดูเรื่อง Eager Loading กับปัญหา N+1

Slow Queries แสดง SQL พร้อม path ของไฟล์ จำนวนครั้ง และเวลาที่ช้าสุด ภาพจาก Zayn Buksh
Slow Jobs และ Slow Outgoing Requests
สองการ์ดนี้ทำงานแบบเดียวกับ Slow Requests การ์ดแรกดู job ใน queue ที่รันนานเกิน threshold การ์ดที่สองดูคำขอที่แอปยิงออกไปข้างนอกผ่าน HTTP client ของ Laravel เช่น เรียก API ของ payment gateway หรือ LINE แล้วรอนาน ใน config เราจัดกลุ่ม URL ด้วย regex ได้ จะได้ไม่เห็น URL ที่ต่างกันแค่ ID แยกเป็นคนละแถว
Application Usage บอกว่า user คนไหนใช้งานหนัก
การ์ดนี้จัดอันดับ 10 user ได้สามแบบ คือ ส่ง request มากที่สุด เจอหน้าช้ามากที่สุด และ dispatch job มากที่สุด ถ้าแอปเป็น backend ให้ frontend หรือแอปมือถือเรียก API การ์ดนี้ช่วยให้เห็นได้เร็วว่า user คนไหนยิง API ถี่ผิดปกติ

Pulse เก็บแค่ user ID แล้วดึงชื่อกับอีเมลจาก model User ตอนแสดงผล
Exceptions บอกว่า error ไหนเกิดบ่อย
การ์ด Exceptions นับว่า exception แต่ละ class เกิดกี่ครั้ง ล่าสุดเมื่อไร และเกิดที่ไฟล์ไหน เหมาะกับดูว่า error ไหนกำลังพุ่ง แต่การ์ดนี้ไม่มี stack trace ไม่มีอีเมลแจ้งเตือน และไม่มีปุ่มปิด issue ถ้าต้องการระบบติดตาม error เต็มรูปแบบ ลองดู watchtower-laravel ที่ผมเขียนไว้ใช้เอง หรือ Sentry ก็ได้
Queues บอกว่า job ค้างหรือ fail ไปเท่าไร
ถ้าแอปส่งงานไปทำใน background การ์ด Queues จะแสดงกราฟของแต่ละ queue ว่ามี job รอ (queued), กำลังทำ (processing), ทำเสร็จ (processed), ปล่อยกลับไปรอคิวใหม่ (released) และ fail กี่ตัว ดูย้อนหลังได้ตามช่วงเวลาที่เลือกบน dashboard

เส้นสีแดงคือ job ที่ fail ถ้าเส้นนี้เริ่มสูงขึ้นก็ถึงเวลาเข้าไปดู log ของ worker
Cache บอก hit rate ของแต่ละ key
การ์ด Cache นับ hit กับ miss รวมทั้งแอป และแยกราย key ถ้าชื่อ key มี ID ปนอยู่ เช่น product:15, product:16 เราจัดกลุ่มด้วย regex ใน config ให้รวมเป็น product:* แถวเดียวได้
Recorders\CacheInteractions::class => [
// ...
'groups' => [
'/:\d+/' => ':*',
],
],
Servers บอก CPU, memory และ disk ของทุกเครื่อง
การ์ด Servers แสดง CPU, memory และพื้นที่ disk ของทุกเครื่องที่รันคำสั่ง pulse:check (ดูหัวข้อถัดไป) ถ้าแอปวางอยู่หลัง load balancer หลายเครื่อง ทุกเครื่องจะรายงานเข้า dashboard เดียวกัน ตั้งชื่อเครื่องเองได้ด้วย PULSE_SERVER_NAME ตอนเปิดตัว ทีม Laravel เล่าว่า Forge ใช้ Pulse ดู request เป็นล้านครั้งโดยไม่ต้องเปิด sampling

เครื่องที่ส่งข้อมูลมาภายใน 30 วินาทีล่าสุดจะมีจุดสีเขียวหน้าชื่อ
มีทั้งธีมสว่างและธีมมืด
dashboard สลับธีมได้จากปุ่มมุมขวาบน หรือปล่อยให้ตามการตั้งค่าของระบบปฏิบัติการ

หน้าเดียวกับภาพแรก แต่เป็นธีมสว่าง ภาพจาก Zayn Buksh
เพิ่มการ์ดของเราเองด้วย Livewire
ถ้าอยากจัดหน้าใหม่ ให้ publish view ของ dashboard ออกมาแก้ ไฟล์จะอยู่ที่ resources/views/vendor/pulse/dashboard.blade.php
php artisan vendor:publish --tag=pulse-dashboard
ในไฟล์นี้การ์ดแต่ละใบเป็น Livewire component เรากำหนดความกว้างด้วย cols ย้ายลำดับ หรือลบการ์ดที่ไม่ใช้ออกได้ โดยไม่ต้อง build JavaScript ใหม่
<x-pulse>
<livewire:pulse.servers cols="full" />
<livewire:pulse.slow-requests cols="6" />
<livewire:pulse.slow-queries cols="6" />
</x-pulse>
การ์ดของเราเองคือ Livewire component ที่ extends Laravel\Pulse\Livewire\Card บันทึกตัวเลขด้วย Pulse::record() แล้วดึงผลรวมมาแสดงด้วย $this->aggregate() รายละเอียดอยู่ในเอกสาร Custom Cards ตอนเปิดตัวทีม Laravel โชว์การ์ดยอดขายตั๋วเครื่องบินที่เขียนเอง วางคู่กับการ์ดมาตรฐาน

การ์ด Flights เป็นการ์ดที่เขียนขึ้นเองสำหรับแอปตัวอย่างในงาน ส่วนการ์ดด้านล่างเป็นการ์ดมาตรฐานของ Pulse
ให้ pulse:check ทำงานตลอดเวลาบน server
การ์ดส่วนใหญ่เก็บข้อมูลเองจาก event ของ Laravel แต่การ์ด Servers ต้องมีคำสั่ง pulse:check คอยวนอ่านค่า CPU, memory และ disk ทุกเครื่องที่อยากเห็นในการ์ดต้องรันคำสั่งนี้
php artisan pulse:check
คำสั่งนี้ทำงานค้างไว้ไม่จบ บน VPS เราให้ Supervisor ดูแลและสั่งรันใหม่ให้เองถ้าหลุด แบบเดียวกับ queue worker
[program:pulse-check]
command=php /var/www/my-app/artisan pulse:check
user=www-data
autostart=true
autorestart=true
redirect_stderr=true
stdout_logfile=/var/www/my-app/storage/logs/pulse-check.log
sudo supervisorctl reread
sudo supervisorctl update
pulse:check ไม่เห็นโค้ดใหม่จนกว่าจะรันใหม่ ในสคริปต์ deploy ให้สั่ง php artisan pulse:restart ด้วย Pulse ส่งสัญญาณ restart ผ่าน cache แอปจึงต้องตั้ง cache driver ไว้ใช้งานได้
ถ้าแอปอยู่บน shared hosting ที่ใช้ cPanel หรือ DirectAdmin มักรัน process ค้างไว้ไม่ได้ การ์ด Servers จะว่างไป แต่การ์ดอื่นยังทำงานครบ
แอป traffic สูง ลดภาระด้วย sampling และ Redis ingest
ค่าเริ่มต้น Pulse เขียนข้อมูลลง database หลังส่ง response ให้ผู้ใช้แล้ว และเก็บไว้ 7 วัน แอปขนาดเล็กถึงกลางใช้ค่าเริ่มต้นได้เลย ถ้า traffic สูงขึ้นมีทางลดภาระสามทาง
- Sampling บันทึกแค่บางส่วน เช่น ตั้ง sample rate ของ recorder
UserRequestsเป็น0.1Pulse จะเก็บประมาณ 10% ของ request แล้วคูณกลับตอนแสดงผล ตัวเลขบน dashboard จะมี~นำหน้าให้รู้ว่าเป็นค่าประมาณ - แยก database ตั้ง
PULSE_DB_CONNECTIONให้ Pulse เขียนลง connection อื่น ไม่ไปแย่งกับ database หลักของแอป - Redis ingest ส่งข้อมูลเข้า Redis stream ก่อน แล้วให้
pulse:workย้ายลง database ทีหลัง ต้องใช้ Redis 6.2 ขึ้นไป และควรใช้ Redis connection แยกจาก queue
PULSE_USER_REQUESTS_SAMPLE_RATE=0.1
PULSE_DB_CONNECTION=pulse
PULSE_INGEST_DRIVER=redis
PULSE_REDIS_CONNECTION=pulse
php artisan pulse:work
pulse:work ทำงานค้างไว้เหมือน pulse:check จึงต้องให้ Supervisor ดูแล และใช้ pulse:restart ตอน deploy เหมือนกัน ถ้าเปิด Redis ingest แล้วลืมรัน pulse:work ข้อมูลจะค้างอยู่ใน Redis และ dashboard จะว่าง
ดูเพิ่ม
Jess Archer เปิดตัว Pulse บนเวที Laracon AU 2023 ดูย้อนหลังได้ที่นี่






