関数による自動ログ記録
例えば、自分のbotをデータベースで自由に動かして、複雑な操作をさせてみたとしよう。いつかはbotがつまずいたり、ミスしたり、予想外の状況に遭遇したりするよね。ログがなければ、botは黙り込んで、何が起きたのか全然分からなくなっちゃう。自動ログ記録があれば:
- エラーや警告が発生したタイミングを追跡できる。
- トラブルの原因や内容を理解できる。
- コードの診断やパフォーマンス改善がしやすくなる。
自動ログ記録を使えば、データベースの「ブラックボックス」を作れて、イベントを記録しながら、まるで一流の探偵みたいにバグを見つけられるんだ。
関数で自動ログ記録を作る方法
- ログ用テーブルを定義する
エラーを記録するには、保存場所が必要だよね。前のレクチャーでerror_logテーブルを作ったよ:
CREATE TABLE error_log (
id SERIAL PRIMARY KEY, -- レコードのユニークID
error_message TEXT NOT NULL, -- エラーメッセージ
error_time TIMESTAMP DEFAULT NOW(), -- エラー発生時刻
function_name TEXT -- エラーを出した関数名
);
このテーブルにはエラー記録に必要なものが全部入ってる:エラーテキスト、発生時刻、どの関数から出たか。
- ログを書き込む関数を作る
次のステップは、エラーをerror_logテーブルに書き込む汎用関数を作ること。この関数は、エラーを記録したい時にいつでも呼び出せるよ。
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);
-- ログ記録成功メッセージ
RAISE NOTICE 'エラーが記録された: %', p_error_message;
END;
$$ LANGUAGE plpgsql;
このコードを解説するね:
p_error_messageとp_function_nameは、エラーメッセージと呼び出し元関数名を受け取るパラメータ。INSERT INTO error_logでテーブルにレコードを追加。RAISE NOTICEで、ログ記録の進行状況を開発者にコンソールで知らせる。
これで最初の課題はクリア!最小限の手間でエラーをテーブルに記録できるようになった。
実際のタスクでlog_error関数を使う
例1: 0で割った時のエラーをログに記録
簡単な割り算をする関数を作ってみよう。ただし、もし分母が0ならエラーをログに記録するよ。
CREATE OR REPLACE FUNCTION divide_numbers(a NUMERIC, b NUMERIC)
RETURNS NUMERIC AS $$
DECLARE
result NUMERIC;
BEGIN
IF b = 0 THEN
-- ログ記録関数を呼び出す
PERFORM log_error('0で割ろうとした!', 'divide_numbers');
-- 例外を発生させる
RAISE EXCEPTION '0で割るのは禁止だよ。';
END IF;
-- 割り算を実行
result := a / b;
RETURN result;
END;
$$ LANGUAGE plpgsql;
- 分母が0なら、
log_error関数が呼ばれてエラーがテーブルに記録される。 - エラー記録の後、
RAISE EXCEPTIONでユーザーに例外を通知。
呼び出し例:
SELECT divide_numbers(10, 0);
結果:
- この関数を0で割る引数で呼ぶと、
error_logテーブルにエラーが記録される。 - ユーザーはコンソールでエラーメッセージを見ることができる。
例2: 無効なデータ挿入時のログ記録
studentsテーブルに新しい学生を追加する関数の例を見てみよう。もし学生名が空なら、イベントをログに記録して処理を中断するよ。
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('');
名前なしで学生を追加しようとすると、関数はerror_logに対応するメッセージでレコードを作るよ。
例3: 警告のログ記録
ユーザーに例外を投げる必要がない場合もある。そんな時は警告だけ記録すればOK。学生の年齢チェック関数を作ってみよう:
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 '年齢チェックOK。';
END;
$$ LANGUAGE plpgsql;
呼び出し例:
SELECT check_age(16);
結果:
error_logテーブルに警告が記録される。- 学生の年齢が18未満だとコンソールで通知される。
ログ記録と例外処理
エラー記録と例外処理を組み合わせた、ちょっと複雑な関数も作ってみよう。例えば、gradesテーブルで学生の成績を再計算するタスクがあるとする。もし1人の学生で処理が失敗しても、エラーをログに記録して、他のデータの処理は続けるよ。
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();
このやり方なら、関数がもっとタフになって、エラーは個別にログに残しつつ、全データの処理を止めずに済むよ。
GO TO FULL VERSION