想像一下,你把自己的 bot 放到資料庫裡自由發揮,讓它跑一些複雜的操作。遲早它會跌倒、出錯,或遇到一些意外狀況。如果沒 log,它可能就這樣安靜地掛掉,你還一頭霧水不知道哪裡出問題。有了自動 log,你可以:
- 追蹤錯誤和警告發生的時候。
- 搞懂失敗的原因和本質。
- 讓你的 code 更好 debug、更有效率。
靠自動 log,你就像有個「黑盒子」一樣,能記錄資料庫裡發生的事,幫你抓 bug,像個頂尖偵探一樣。
怎麼用 function 實現自動 log
- 先定義一個 log table
要記錄錯誤,咱們得有地方存。上一堂課我們已經建好 error_log 這個 table:
CREATE TABLE error_log (
id SERIAL PRIMARY KEY, -- 唯一的紀錄 ID
error_message TEXT NOT NULL, -- 錯誤訊息
error_time TIMESTAMP DEFAULT NOW(), -- 發生錯誤的時間
function_name TEXT -- 出錯的 function 名稱
);
這個 table 有你需要的所有欄位:錯誤內容、時間、還有是哪個 function 出錯。
- 寫一個記錄 log 的 function
下一步——寫一個通用的 function,能把錯誤寫進 error_log。每次你想記錄錯誤時就 call 它。
CREATE OR REPLACE FUNCTION log_error(p_error_message TEXT, p_function_name TEXT)
RETURNS VOID AS $$
BEGIN
INSERT INTO error_log (error_message, function_name)
VALUES (p_error_message, p_function_name);
-- 成功記錄 log 的訊息
RAISE NOTICE '已記錄錯誤: %', p_error_message;
END;
$$ LANGUAGE plpgsql;
來拆解一下這段 code:
p_error_message和p_function_name是 function 的參數,分別接收錯誤訊息和出錯的 function 名稱。INSERT INTO error_log把紀錄寫進 table。RAISE NOTICE會在 console 顯示訊息,讓開發者知道 log 有被記下來。
這樣第一步就搞定啦:你可以很輕鬆把錯誤寫進 table。
在實際任務裡用 log_error function
範例 1:除以零時記錄錯誤
來寫個 function,做簡單的除法,如果分母是 0 就記錄錯誤。
CREATE OR REPLACE FUNCTION divide_numbers(a NUMERIC, b NUMERIC)
RETURNS NUMERIC AS $$
DECLARE
result NUMERIC;
BEGIN
IF b = 0 THEN
-- 呼叫 log function
PERFORM log_error('試圖除以零!', 'divide_numbers');
-- 丟出 exception
RAISE EXCEPTION '不允許除以零。';
END IF;
-- 執行除法
result := a / b;
RETURN result;
END;
$$ LANGUAGE plpgsql;
- 如果分母是 0,就會呼叫
log_error,把錯誤寫進 table。 - 寫完 log 之後會丟出
RAISE EXCEPTION,讓使用者知道。
呼叫範例:
SELECT divide_numbers(10, 0);
結果:
- 這樣呼叫 function,遇到除以零就會在
error_logtable 裡有一筆錯誤紀錄。 - 使用者會在 console 看到錯誤訊息。
範例 2:插入不合法資料時記錄 log
來看一個 function,會把新學生加進 students table。如果學生名字是空的,我們就記錄 log 並中斷。
CREATE OR REPLACE FUNCTION add_student(p_name TEXT)
RETURNS VOID AS $$
BEGIN
IF p_name IS NULL OR p_name = '' THEN
PERFORM log_error('必須填寫學生名字!', 'add_student');
RAISE EXCEPTION '學生名字不能是空的。';
END IF;
INSERT INTO students (name) VALUES (p_name);
END;
$$ LANGUAGE plpgsql;
呼叫範例:
SELECT add_student('');
如果你試著加一個沒名字的學生,function 會在 error_log 寫一筆紀錄。
範例 3:記錄警告
有時候不用丟 exception,只要記錄個警告就好。來寫個 function,檢查學生年齡:
CREATE OR REPLACE FUNCTION check_age(p_age INT)
RETURNS VOID AS $$
BEGIN
IF p_age < 18 THEN
-- 記錄警告,但不中斷
PERFORM log_error('學生年齡小於 18。', 'check_age');
RAISE NOTICE '警告:學生年齡小於 18。';
END IF;
RAISE NOTICE '年齡檢查通過。';
END;
$$ LANGUAGE plpgsql;
呼叫範例:
SELECT check_age(16);
結果:
- 會在
error_logtable 寫一筆警告紀錄。 - console 會通知學生年齡小於 18。
log 和 exception 處理
來把錯誤記錄和 exception 處理結合在一個進階 function 裡。假設你要重算 grades table 裡所有學生的分數。如果有學生沒算成功,錯誤會被記錄下來,但整個操作不會中斷。
CREATE OR REPLACE FUNCTION recalculate_grades()
RETURNS VOID AS $$
DECLARE
student RECORD;
BEGIN
FOR student IN SELECT * FROM students LOOP
BEGIN
-- 範例任務:更新學生分數
UPDATE grades SET final_grade = final_grade + 1
WHERE student_id = student.id;
RAISE NOTICE '已更新學生 % 的分數', student.name;
EXCEPTION WHEN OTHERS THEN
-- 記錄錯誤並繼續
PERFORM log_error('無法更新學生 ' || student.name || ' 的分數', 'recalculate_grades');
END;
END LOOP;
END;
$$ LANGUAGE plpgsql;
呼叫範例:
SELECT recalculate_grades();
這種寫法讓你的 function 更穩健,因為錯誤會被單獨記錄,不會讓所有資料都停下來。
GO TO FULL VERSION