CodeGym /Các khóa học /SQL SELF /Ghi log lỗi: các mức log và định dạng thông báo

Ghi log lỗi: các mức log và định dạng thông báo

SQL SELF
Mức độ , Bài học
Có sẵn

Ghi log lỗi: các mức log và định dạng thông báo

Bây giờ tụi mình giống như mấy điệp viên bí mật ấy — function và procedure của mình đang làm nhiệm vụ: xử lý dữ liệu, tính toán hoặc đơn giản là làm phép thuật trong database. Nhưng làm sao biết nếu có gì đó không ổn? Làm sao biết ở bước nào cái "bồn tắm massage cho dữ liệu" bị hỏng? Lúc này ghi log và xử lý lỗi sẽ cứu cánh cho bạn.

Nhớ không, tụi mình đã bắt đầu làm quen với việc "giao tiếp" và ghi log trong PostgreSQL và PL/pgSQL rồi nè:

  • RAISE NOTICE: giọng điệu nhẹ nhàng, thân thiện — "Ê, mọi thứ ổn nha, nhưng chắc bạn sẽ muốn xem cái này đó".
  • RAISE WARNING: giọng hơi lớn hơn — "Ui, có gì đó lạ lạ, chắc bạn nên kiểm tra thử".
  • RAISE EXCEPTION: còi báo động luôn — "DỪNG LẠI! Thuật toán gặp nguy! Đã dừng thực thi để mọi thứ không toang".

Mỗi mức này đều có mục đích riêng, và quan trọng là chọn đúng cái nào để dùng.

Đây là ví dụ mấy thông báo này trong code:

DO $$
BEGIN
    -- Mức NOTICE (mọi thứ ổn, chỉ là thông báo thôi)
    RAISE NOTICE 'Chỉ là thông báo: bắt đầu xử lý dữ liệu';

    -- Mức WARNING (có gì đó nghi ngờ)
    RAISE WARNING 'Cảnh báo: định dạng dữ liệu trong cột có thể không đúng';

    -- Mức EXCEPTION (lỗi nghiêm trọng)
    RAISE EXCEPTION 'Lỗi: giá trị đầu vào không hợp lệ!';
END $$;

Khi nào dùng cái nào:

  • RAISE NOTICE — để debug và in thông tin nhẹ nhàng.
  • RAISE WARNING — để cảnh báo về dữ liệu có thể sai.
  • RAISE EXCEPTION — khi có lỗi nghiêm trọng, cần dừng function lại.

Xử lý lỗi với RAISE EXCEPTION

RAISE EXCEPTION — giống như phanh khẩn cấp vậy. Nếu có gì đó không ổn, bạn có thể dừng function và báo lỗi liền.

Nhắc lại, dùng cơ bản như này nè:

RAISE EXCEPTION 'Thông báo lỗi của bạn';

Nhưng để thông báo chi tiết hơn, bạn có thể dùng biến luôn:

DECLARE
    input_value INTEGER;
BEGIN
    input_value := NULL;

    IF input_value IS NULL THEN
        RAISE EXCEPTION 'Lỗi: giá trị đầu vào NULL. Mong đợi giá trị INTEGER';
    END IF;
END;

Định dạng thông báo

Bạn có thể chèn biến trực tiếp vào thông báo luôn:

DECLARE
    var1 TEXT := 'Dữ liệu';
    var2 INTEGER := 42;
BEGIN
    RAISE EXCEPTION 'Lỗi khi xử lý % với ID %', var1, var2;
END;

Kết quả: Lỗi khi xử lý Dữ liệu với ID 42.

Ví dụ: kiểm tra dữ liệu

Giả sử bạn có procedure nhận tuổi của người dùng. Nếu tuổi âm thì hợp lý là báo lỗi luôn:

CREATE OR REPLACE FUNCTION validate_age(age INTEGER)
RETURNS VOID AS $$
BEGIN
    IF age < 0 THEN
        RAISE EXCEPTION 'Tuổi không thể âm: %', age;
    END IF;
END;
$$ LANGUAGE plpgsql;

-- Gọi function
SELECT validate_age(-5);  -- Sẽ báo lỗi

Thông báo với RAISE NOTICE

Nếu RAISE EXCEPTION là còi báo động thì RAISE NOTICE là cái vỗ vai thân thiện. Dùng mức này để thêm comment cho dễ hiểu chuyện gì đang xảy ra trong function.

Khi nào dùng RAISE NOTICE:

  • In thông tin debug (ví dụ, trạng thái biến hiện tại).
  • Thông báo bắt đầu bước nào đó hoặc kết quả tính toán.

Ví dụ: thông báo thông tin

CREATE OR REPLACE FUNCTION calculate_discount(price NUMERIC, discount_rate NUMERIC)
RETURNS NUMERIC AS $$
DECLARE
    final_price NUMERIC;
BEGIN
    RAISE NOTICE 'Giá trước khi giảm: %', price;
    RAISE NOTICE 'Tỷ lệ giảm giá: %', discount_rate;

    final_price := price - (price * discount_rate);

    RAISE NOTICE 'Giá cuối cùng: %', final_price;

    RETURN final_price;
END;
$$ LANGUAGE plpgsql;

-- Gọi function
SELECT calculate_discount(100, 0.2);
-- Sẽ in ra:
-- NOTICE: Giá trước khi giảm: 100
-- NOTICE: Tỷ lệ giảm giá: 0.2
-- NOTICE: Giá cuối cùng: 80

Ứng dụng thực tế: lập kế hoạch và ghi log

Giả sử bạn có procedure xử lý dữ liệu phức tạp, và bạn muốn biết nó đang ở bước nào:

CREATE OR REPLACE FUNCTION process_data_step_by_step()
RETURNS VOID AS $$
BEGIN
    RAISE NOTICE 'Bước 1: Chuẩn bị dữ liệu';
    -- Logic cho bước đầu tiên

    RAISE NOTICE 'Bước 2: Kiểm tra dữ liệu';
    -- Logic cho bước thứ hai

    RAISE NOTICE 'Bước 3: Lưu dữ liệu';
    -- Logic cho bước thứ ba
END;
$$ LANGUAGE plpgsql;

-- Gọi function
SELECT process_data_step_by_step();
-- Log sẽ hiển thị từng bước thực hiện

Thêm một ví dụ nữa nha. Giả sử có cửa hàng chỉ giảm giá cho đơn hàng trên một mức nhất định:

CREATE OR REPLACE FUNCTION apply_discount(order_amount NUMERIC)
RETURNS NUMERIC AS $$
BEGIN
    IF order_amount < 50 THEN
        RAISE EXCEPTION 'Lỗi: tổng đơn hàng phải ít nhất 50, hiện tại: %', order_amount;
    END IF;

    RETURN order_amount * 0.9;  -- Áp dụng giảm giá 10%
END;
$$ LANGUAGE plpgsql;

-- Gọi function
SELECT apply_discount(30);  -- Lỗi: tổng đơn hàng phải ít nhất 50

Lỗi thường gặp

Lỗi 1: ghi log mà không có tham số.

Nhìn không rõ ràng, nhất là trong procedure dài:

RAISE NOTICE 'Đã xảy ra lỗi';  -- Tại sao? Ở đâu? Như thế nào?

Khuyến nghị: luôn thêm ngữ cảnh:

RAISE NOTICE 'Lỗi trong function process_data(): giá trị đầu vào: %', input_value;

Lỗi 2: dùng RAISE EXCEPTION chỗ chỉ cần RAISE WARNING.

Nếu lạm dụng exception, code sẽ bị dừng liên tục, rất khó xử lý dữ liệu.

Mẹo: dùng mức log hợp lý. Debug thì chọn NOTICE, còn lỗi nghiêm trọng thì mới EXCEPTION.

Lỗi 3: không ghi log gì cả.

Giống như mò chìa khóa trong phòng tối vậy. Không có log thì debug mấy process phức tạp gần như bất khả thi.

Mẹo: thêm RAISE NOTICE ở các bước quan trọng trong function, nhất là khi function dài và phức tạp.

Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION