CodeGym /課程 /SQL SELF /透過 function 自動記錄 log

透過 function 自動記錄 log

SQL SELF
等級 52 , 課堂 3
開放

想像一下,你把自己的 bot 放到資料庫裡自由發揮,讓它跑一些複雜的操作。遲早它會跌倒、出錯,或遇到一些意外狀況。如果沒 log,它可能就這樣安靜地掛掉,你還一頭霧水不知道哪裡出問題。有了自動 log,你可以:

  • 追蹤錯誤和警告發生的時候。
  • 搞懂失敗的原因和本質。
  • 讓你的 code 更好 debug、更有效率。

靠自動 log,你就像有個「黑盒子」一樣,能記錄資料庫裡發生的事,幫你抓 bug,像個頂尖偵探一樣。

怎麼用 function 實現自動 log

  1. 先定義一個 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 出錯。

  1. 寫一個記錄 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:

  1. p_error_messagep_function_name 是 function 的參數,分別接收錯誤訊息和出錯的 function 名稱。
  2. INSERT INTO error_log 把紀錄寫進 table。
  3. 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_log table 裡有一筆錯誤紀錄。
  • 使用者會在 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_log table 寫一筆警告紀錄。
  • 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 更穩健,因為錯誤會被單獨記錄,不會讓所有資料都停下來。

留言
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION