CodeGym /課程 /SQL SELF /錯誤日誌:日誌等級與訊息格式

錯誤日誌:日誌等級與訊息格式

SQL SELF
等級 55 , 課堂 2
開放

我們現在就像秘密特務一樣——我們的 function 跟 procedure 都在執行任務:處理資料、做計算,或是在資料庫裡搞點魔法。但如果哪裡出錯了怎麼辦?怎麼知道是哪個階段壞掉了這個「資料按摩浴缸」?這時候 日誌記錄跟錯誤處理 就派上用場啦。

還記得我們之前有聊過怎麼在 PostgreSQL 跟 PL/pgSQL 裡「溝通」跟寫 log 嗎:

  • RAISE NOTICE:很 chill、很朋友的語氣——「欸,這裡沒事啦,不過你可能想看一下」。
  • RAISE WARNING:語氣大聲一點——「喂,這裡怪怪的,你要不要來瞄一下」。
  • RAISE EXCEPTION:警報大響——「STOP!演算法出事啦!我們停下來,不然全炸了」。

這些等級各有用途,選對很重要。

訊息在 code 裡長這樣:

DO $$
BEGIN
    -- NOTICE 等級(都正常,只是通知)
    RAISE NOTICE '只是通知:開始處理資料';

    -- WARNING 等級(有點可疑)
    RAISE WARNING '警告:欄位的資料格式可能不正確';

    -- EXCEPTION 等級(嚴重錯誤)
    RAISE EXCEPTION '錯誤:輸入值不合法!';
END $$;

什麼時候用:

  • RAISE NOTICE — debug 跟一般資訊輸出。
  • RAISE WARNING — 提醒有可能資料怪怪的。
  • RAISE EXCEPTION — 發生嚴重錯誤,function 必須停下來。

RAISE EXCEPTION 處理錯誤

RAISE EXCEPTION 就是你的緊急煞車。如果出事了,你可以直接停掉 function,然後丟出錯誤訊息。

最基本的用法長這樣:

RAISE EXCEPTION '你的錯誤訊息';

但如果想讓訊息更有料,可以用變數:

DECLARE
    input_value INTEGER;
BEGIN
    input_value := NULL;

    IF input_value IS NULL THEN
        RAISE EXCEPTION '錯誤:輸入值是 NULL。預期要 INTEGER';
    END IF;
END;

訊息格式化

你可以直接把變數塞進訊息裡:

DECLARE
    var1 TEXT := '資料';
    var2 INTEGER := 42;
BEGIN
    RAISE EXCEPTION '處理 % 時發生錯誤,ID 是 %', var1, var2;
END;

輸出:處理 資料 時發生錯誤,ID 是 42

範例:資料驗證

想像一下你有個 procedure 要收人的年齡。如果年齡是負的,當然要丟錯誤:

CREATE OR REPLACE FUNCTION validate_age(age INTEGER)
RETURNS VOID AS $$
BEGIN
    IF age < 0 THEN
        RAISE EXCEPTION '年齡不能是負的:%', age;
    END IF;
END;
$$ LANGUAGE plpgsql;

-- 呼叫 function
SELECT validate_age(-5);  -- 會丟錯誤

RAISE NOTICE 通知

如果 RAISE EXCEPTION 是警報,RAISE NOTICE 就像朋友拍拍你肩膀。這個等級可以加註解,讓你知道 function 裡發生什麼事。

什麼時候用 RAISE NOTICE

  • 輸出 debug 資訊(像是變數目前的值)。
  • 告訴你某個階段開始或計算結果。

範例:資訊訊息

CREATE OR REPLACE FUNCTION calculate_discount(price NUMERIC, discount_rate NUMERIC)
RETURNS NUMERIC AS $$
DECLARE
    final_price NUMERIC;
BEGIN
    RAISE NOTICE '折扣前價格:%', price;
    RAISE NOTICE '折扣率:%', discount_rate;

    final_price := price - (price * discount_rate);

    RAISE NOTICE '最終價格:%', final_price;

    RETURN final_price;
END;
$$ LANGUAGE plpgsql;

-- 呼叫 function
SELECT calculate_discount(100, 0.2);
-- 會輸出:
-- NOTICE: 折扣前價格:100
-- NOTICE: 折扣率:0.2
-- NOTICE: 最終價格:80

實戰:規劃與日誌

假設你有個很複雜的資料處理 procedure,你想知道現在跑到哪個階段:

CREATE OR REPLACE FUNCTION process_data_step_by_step()
RETURNS VOID AS $$
BEGIN
    RAISE NOTICE '步驟 1:準備資料';
    -- 這裡寫第一階段的邏輯

    RAISE NOTICE '步驟 2:驗證資料';
    -- 這裡寫第二階段的邏輯

    RAISE NOTICE '步驟 3:儲存資料';
    -- 這裡寫第三階段的邏輯
END;
$$ LANGUAGE plpgsql;

-- 呼叫 function
SELECT process_data_step_by_step();
-- log 裡會看到每個步驟

再來一個例子。 想像有個商店,只有訂單金額超過某個數才有折扣:

CREATE OR REPLACE FUNCTION apply_discount(order_amount NUMERIC)
RETURNS NUMERIC AS $$
BEGIN
    IF order_amount < 50 THEN
        RAISE EXCEPTION '錯誤:訂單金額必須至少 50,現在是:%', order_amount;
    END IF;

    RETURN order_amount * 0.9;  -- 給 10% 折扣
END;
$$ LANGUAGE plpgsql;

-- 呼叫 function
SELECT apply_discount(30);  -- 錯誤:訂單金額必須至少 50

常見錯誤

錯誤 1: 記 log 沒帶參數。

這樣在大 procedure 裡根本看不懂:

RAISE NOTICE '發生錯誤';  -- 為什麼?哪裡?怎麼了?

建議: 一定要加 context:

RAISE NOTICE 'process_data() 發生錯誤:輸入值:%', input_value;

錯誤 2: 該用 RAISE WARNING 的地方卻用 RAISE EXCEPTION

如果你太愛丟 exception,code 會一直被中斷,資料處理超麻煩。

小撇步: 日誌等級要用對。debug 用 NOTICE,真的嚴重才用 EXCEPTION

錯誤 3: 完全沒記 log。

這就像在黑暗房間找鑰匙。沒 log,debug 複雜流程根本不可能。

小撇步: function 關鍵階段一定要加 RAISE NOTICE,尤其是大又複雜的 function。

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