Hãy tưởng tượng bạn đang làm việc với database, xử lý cả đống transaction, nhưng tự nhiên có gì đó không ổn. Người dùng than phiền, business la hét, còn bạn thì chẳng biết chính xác chuyện gì vừa xảy ra. Đó là lúc log lỗi chuẩn phát huy tác dụng. Nó giúp bạn:
- Lưu lỗi vào database để sau này phân tích.
- Dễ dàng lấy lại chi tiết lỗi: thời điểm xảy ra, message và dữ liệu liên quan.
- Cải thiện code của bạn bằng cách xử lý các lỗi phổ biến mà bạn phát hiện khi phân tích log.
Nói ngắn gọn, log lỗi giống như camera giám sát cho database của bạn: bạn thấy hết mọi thứ sai sót và thời điểm nó xảy ra.
Tạo bảng error_log
Bắt đầu từ nền móng nhé. Ta cần tạo một bảng để lưu thông tin về lỗi. Bảng này sẽ có các trường sau:
id— mã định danh duy nhất của lỗi.error_message— nội dung lỗi.error_time— thời điểm lỗi xảy ra.- (Tùy chọn)
context— ngữ cảnh lỗi, nếu bạn muốn lưu chi tiết hơn.
CREATE TABLE error_log (
id SERIAL PRIMARY KEY, -- Mã định danh duy nhất của lỗi
error_message TEXT NOT NULL, -- Nội dung lỗi dạng text
error_time TIMESTAMP NOT NULL DEFAULT NOW(), -- Thời điểm lỗi xảy ra
context JSONB -- Dữ liệu bổ sung về lỗi
);
Ở đây có gì?
id SERIAL PRIMARY KEY— trường này tự động tạo mã định danh duy nhất cho mỗi bản ghi.error_message TEXT NOT NULL— nơi lưu nội dung lỗi. Trường này bắt buộc phải có.error_time TIMESTAMP NOT NULL DEFAULT NOW()— trường lưu thời điểm xảy ra lỗi. Nếu bạn không truyền giá trị, nó sẽ tự động lấy thời gian hiện tại nhờDEFAULT NOW().context JSONB— tuỳ chọn, để lưu thông tin bổ sung, ví dụ dữ liệu về thao tác gây ra lỗi.
Sau khi chạy lệnh CREATE TABLE, database của bạn sẽ có cấu trúc để lưu log lỗi.
Ví dụ ghi lỗi vào bảng
Bây giờ bảng đã có, ta chuyển sang phần thực hành: lưu thông tin lỗi vào bảng. Ta sẽ viết một function để ghi nội dung lỗi và thời điểm vào error_log.
Ví dụ function ghi lỗi
CREATE OR REPLACE FUNCTION log_error(p_error_message TEXT, p_context JSONB DEFAULT NULL)
RETURNS VOID AS $$
BEGIN
INSERT INTO error_log (error_message, context)
VALUES (p_error_message, p_context);
END;
$$ LANGUAGE plpgsql;
p_error_message— tham số đầu vào, truyền nội dung lỗi.p_context— tham số tuỳ chọn để truyền dữ liệu bổ sung. Mặc định làNULL.INSERT INTO error_log (error_message, context)— thêm bản ghi mới vào bảngerror_log.DEFAULT NULL— nếu không truyền context, trường này sẽ làNULL.
Bây giờ, nếu muốn ghi một lỗi, bạn chỉ cần gọi function như sau:
SELECT log_error('Lỗi khi thực hiện truy vấn', '{"query": "SELECT * FROM data"}');
Lệnh này sẽ thêm một bản ghi mới vào bảng error_log.
Tự động log lỗi
Việc mỗi lần đều phải SELECT log_error(...) bằng tay thì hơi phiền. Hãy tự động hoá quá trình này bằng cách dùng exception handling.
Ví dụ function có xử lý exception.
CREATE OR REPLACE FUNCTION divide_numbers(a NUMERIC, b NUMERIC)
RETURNS NUMERIC AS $$
DECLARE
result NUMERIC;
BEGIN
-- Thử thực hiện phép chia
result := a / b;
-- Trả về kết quả
RETURN result;
EXCEPTION
WHEN division_by_zero THEN
-- Ghi log lỗi và truyền context
PERFORM log_error('Chia cho số 0', jsonb_build_object('a', a, 'b', b));
-- Thông báo lỗi cho người dùng
RAISE EXCEPTION 'Không thể chia % cho % — mẫu số bằng 0', a, b;
END;
$$ LANGUAGE plpgsql;
- Kiểm tra phép chia: thực hiện
result := a / b. Nếub = 0, PostgreSQL sẽ ném exceptiondivision_by_zero. - Exception handler (
EXCEPTION) sẽ bắt lỗi này. - Bên trong handler, ta gọi function
log_errorđể ghi lỗi vào bảng cùng với tham số gây ra lỗi. - Sau khi ghi log, dùng
RAISE EXCEPTIONđể báo lỗi cho người dùng luôn.
Ví dụ gọi:
SELECT divide_numbers(10, 0);
Kết quả:
- Người dùng sẽ thấy lỗi:
Không thể chia 10 cho 0 — mẫu số bằng 0. - Bảng
error_logsẽ có bản ghi về lỗi chia cho số 0.
GO TO FULL VERSION