Laravel là một Framework PHP mạnh mẽ và linh hoạt, giúp cho việc phát triển ứng dụng trở nên đơn giản và dễ dàng hơn.

Một trong những tính năng hữu ích của Laravel là khả năng ghi nhật ký truy vấn SQL. Ghi nhật ký truy vấn SQL là một cách tuyệt vời để theo dõi hiệu suất ứng dụng của bạn và xác định các vấn đề tiềm ẩn.

Trong hướng dẫn này, chúng ta sẽ cùng nhau khám phá cách ghi nhật ký truy vấn cơ sở dữ liệu trong Laravel. Để quản lý việc ghi nhật kí truy vấn SQL một cách hiệu quả, chúng ta sẽ sử dụng ServiceProvider.

Để tạo một ServiceProvider mới để quản lý truy vấn SQL, chúng ta sử dụng command sau:

php artisan make:provider DatabaseQueryServiceProvider

Sau khi tạo ServiceProvider, chúng ta cần đăng ký nó trong config/app.php. Để làm điều này, chúng ta thêm dòng sau vào mảng providers:

'providers' => [
    ...
    App\Providers\DatabaseQueryServiceProvider::class,
],

Tiếp theo, bạn hãy chỉnh sửa mã nguồn DatabaseQueryServiceProvider.php như sau:

<?php

namespace App\Providers;

use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\ServiceProvider;
use Illuminate\Database\Events\TransactionBeginning;
use Illuminate\Database\Events\TransactionCommitted;
use Illuminate\Database\Events\TransactionRolledBack;

class DatabaseQueryServiceProvider extends ServiceProvider
{
    // Define the log channel for database queries
    protected $channel = 'sql';

    /**
     * Register services.
     */
    public function register(): void
    {
        // Store the channel in a variable for use in closures
        $channel = $this->channel;

        // Check if the log channel is enabled in the configuration
        if (!config("logging.channels.{$channel}.enable")) {
            return;
        }

        // Listen to database queries and log them
        DB::listen(static function ($query) use ($channel) {
            $sql            = vsprintf(str_replace('?', "'%s'", $query->sql), $query->bindings);
            $execution_time = $query->time;
            // Determine log level based on execution time
            $log_level      = $execution_time > config("logging.channels.{$channel}.slow_query_time") ? 'warning' : 'debug';
            // Log the query with execution time and SQL statement
            Log::channel($channel)->$log_level(sprintf('%.2f ms, SQL: %s;', $execution_time, $sql));
        });

        // Listen to the beginning of a database transaction and log it
        Event::listen(static function (TransactionBeginning $event) use ($channel) {
            Log::channel($channel)->debug('BEGIN TRANSACTION');
        });

        // Listen to the successful commit of a database transaction and log it
        Event::listen(static function (TransactionCommitted $event) use ($channel) {
            Log::channel($channel)->debug('COMMIT');
        });

        // Listen to the rollback of a database transaction and log it
        Event::listen(static function (TransactionRolledBack $event) use ($channel) {
            Log::channel($channel)->debug('ROLLBACK');
        });
    }
}

Tiếp tục, chúng ta sẽ tạo một channel logging dành riêng cho việc ghi lại nhật kí truy vấn SQL trong hệ thống, bạn hãy thêm mã nguồn sau vào config/logging.php:

'channels' => [
    ...
    'sql' => [
        'driver' => 'daily',
        'path' => storage_path('logs/query.log'),
        'level' => env('LOG_LEVEL', 'debug'),
        'enable' => env('LOG_SQL_ENABLE', false),
        'slow_query_time' => env('LOG_SQL_SLOW_QUERY_TIME', 2000),
    ],
],

Để sử dụng tính năng ghi nhật kí truy vấn SQL, bạn cần cấu hình các giá trị bên dưới vào .env:

LOG_SQL_ENABLE=true
LOG_SQL_SLOW_QUERY_TIME=1000

Bây giờ, để xem mọi thứ có hoạt động như mong đợi không, hãy tạo UserController.php với nội dung sau:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;
use App\Models\User;
use Illuminate\Support\Str;
use Illuminate\Support\Facades\DB;

class UserController extends Controller
{
    /**
     * Display a listing of the resource.
     */
    public function index()
    {
        # Rollback Example
        try {
            // Begin a database transaction
            DB::beginTransaction();
            // Create a new User with provided data
            User::create([
                'name' => 'ManhDanBlogs (Beater)',
                'email' => '[email protected]',
                'email_verified_at' => now(),
                'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
                'remember_token' => Str::random(10),
            ]);
            // Simulate an error (division by zero) to trigger rollback
            $bug = 1 / 0;
            // Commit the transaction if no exception is thrown
            DB::commit();
        } catch (Throwable $th) {
            // Log and report any exceptions
            report($th);
            // Rollback the transaction in case of an exception
            DB::rollBack();
        }

        # Not Rollback Example
        try {
            // Begin a database transaction
            DB::beginTransaction();
            // Create a new User with provided data
            $user = User::create([
                'name' => 'ManhDanBlogs (Beater)',
                'email' => '[email protected]',
                'email_verified_at' => now(),
                'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password
                'remember_token' => Str::random(10),
            ]);
            // Commit the transaction if no exception is thrown
            DB::commit();
        } catch (Throwable $th) {
            // Log and report any exceptions
            report($th);
            // Rollback the transaction in case of an exception
            DB::rollBack();
        }
        // Attempt to retrieve the User with the provided ID (outside of the transaction)
        User::find($user->id);
    }
    .....
}

Sau khi chạy mã nguồn UserController.php, bạn sẽ thấy chi tiết tất cả truy vấn SQL được thực hiện, bao gồm cả truy vấn thành công và thất bại, với thời gian thực thi để phân tích hiệu suất.

-rw-r--r-- 1 root root 2,3K Thg 12  1 08:11 query-2023-12-04.log
tail -fn 1000 query-2023-12-04.log 
[2023-12-04 14:17:02] local.DEBUG: BEGIN TRANSACTION  
[2023-12-04 14:17:02] local.WARNING: 1.95 ms, SQL: insert into `users` (`name`, `email`, `password`, `updated_at`, `created_at`) values ('ManhDanBlogs (Beater)', '[email protected]', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', '2023-12-04 14:17:02', '2023-12-04 14:17:02');  
[2023-12-04 14:17:02] local.DEBUG: ROLLBACK  
[2023-12-04 14:17:02] local.DEBUG: BEGIN TRANSACTION  
[2023-12-04 14:17:02] local.DEBUG: 0.20 ms, SQL: insert into `users` (`name`, `email`, `password`, `updated_at`, `created_at`) values ('ManhDanBlogs (Beater)', '[email protected]', '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', '2023-12-04 14:17:02', '2023-12-04 14:17:02');  
[2023-12-04 14:17:02] local.DEBUG: COMMIT  
[2023-12-04 14:17:02] local.DEBUG: 0.46 ms, SQL: select * from `users` where `users`.`id` = '1007' limit 1; 

Bạn hãy tận hưởng cảm giác an tâm và chủ động với tính năng ghi nhật kí truy vấn SQL mạnh mẽ này. Giờ đây, tối ưu hóa truy vấn và sửa lỗi trở nên đơn giản hơn bao giờ hết! 🚀🚀🚀🚀🚀

CÓ THỂ BẠN QUAN TÂM

Amazon S3 Pre-Signed URL with DropzoneJs in Laravel

Amazon S3 Pre-Signed URL with DropzoneJs in Laravel

Chức năng upload file hay hình ảnh là một chức năng rất phổ biến, hầu hết các dự án đều có chức năng này. Đa số các nhà phát triển khi thực hiện chức năng upload file, thường sẽ sử dụng cách làm nh...

How to insert into a database at lightning speed?

How to insert into a database at lightning speed?

Trong quá trình thực hiện dự án cho công ty, một trong những yêu cầu đặt ra là import dữ liệu từ file CSV (chứa dữ liệu từ hệ thống cũ) vào cơ sở dữ liệu MySQL của hệ thống mới. Do sự thay đổi cấu...

Laravel 9 REST API With Sanctum Authentication

Laravel 9 REST API With Sanctum Authentication

Laravel Sanctum Laravel Sanctum cung cấp một hệ thống authentication đơn giản cho các SPA, ứng dụng Mobile và API đơn giản sử dụng token. Sanctum cho phép ứng dụng của bạn phát hành các mã token...

Integrating elFinder into TinyMCE 6 in Laravel

Integrating elFinder into TinyMCE 6 in Laravel

TinyMCE TinyMCE là một trình soạn thảo WYSIWYG được xây dựng trên nền tảng Javascript, được phát triển dưới dạng mã nguồn mở theo giấy phép MIT bởi Tiny Technologies Inc. TinyMCE cho phép người d...

Laravel Factories, Seeder

Laravel Factories, Seeder

Trong bài viết này, tôi sẽ hướng dẫn các bạn về cách tạo dữ liệu giả trong cơ sở dữ liệu bằng cách sử dụng Laravel Factory và Seed trong Database Seeder. Để tạo model factory, bạn cần chạy lệnh sau...

Laravel Custom Request

Laravel Custom Request

Nếu bạn có một form để người dùng nhập dữ liệu và bạn muốn kiểm tra dữ liệu đầu vào trước khi lưu xuống database chẳng hạn thì bạn có 2 cách sau đây: Cách 1: Bạn thêm validate trực tiếp vào hàm sto...

Laravel Mail Sending Redirector Listener

Laravel Mail Sending Redirector Listener

Trong quá trình phát triển web, việc gửi email là một chức năng quan trọng để thông báo, đặt lại mật khẩu, hoặc tương tác với người dùng. Tuy nhiên, khi chúng ta đang trong quá trình phát triển, vi...

Generate PDF with Header and Footer on every page in Laravel

Generate PDF with Header and Footer on every page in Laravel

Hôm nay, trong bài viết này mình sẽ chia sẻ với các bạn cách dompdf mà header và footer sẽ được hiển thị ở tất cả các trang. Đây cũng là một vấn đề khá phổ biến, khi chúng ta phát triển các tính năng...

Laravel One to One Eloquent Relationship

Laravel One to One Eloquent Relationship

Mối quan hệ một-một là một mối quan hệ rất cơ bản. Trong hướng dẫn này, tôi sẽ hướng dẫn bạn cách tạo dữ liệu và truy xuất dữ liệu bằng Eloquent Model. Trong hướng dẫn này, tôi sẽ tạo hai bảng là u...

ManhDanBlogs