Уяви, що ти відправив свого бота у вільне плавання по базі даних, щоб він виконував складні операції. Рано чи пізно він спіткнеться, помилиться або натрапить на неочікувану ситуацію. Без логування він може просто замовкнути, а ти залишишся в здивуванні, що пішло не так. Автоматичне логування допомагає:
- Відслідковувати виникнення помилок і попереджень.
- Розуміти природу і причини збоїв.
- Покращувати діагностику і продуктивність коду.
Завдяки автоматичному логуванню ти створюєш "чорну скриньку", яка фіксує події в базі даних і допомагає знаходити помилки, ніби ти топовий детектив.
Створення автоматичного логування через функції
- Визначаємо таблицю для логів
Щоб логувати помилки, нам потрібно місце для їх зберігання. Ми вже створили таблицю error_log у попередній лекції:
CREATE TABLE error_log (
id SERIAL PRIMARY KEY, -- Унікальний ідентифікатор запису
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.
CREATE OR REPLACE FUNCTION divide_numbers(a NUMERIC, b NUMERIC)
RETURNS NUMERIC AS $$
DECLARE
result NUMERIC;
BEGIN
IF b = 0 THEN
-- Викликаємо функцію логування
PERFORM log_error('Спроба ділення на нуль!', 'divide_numbers');
-- Генеруємо виключення
RAISE EXCEPTION 'Ділення на нуль заборонено.';
END IF;
-- Виконуємо ділення
result := a / b;
RETURN result;
END;
$$ LANGUAGE plpgsql;
- Якщо знаменник дорівнює 0, викликається функція
log_error, яка записує помилку в таблицю. - Після запису помилки генерується виключення
RAISE EXCEPTION, щоб повідомити користувача.
Приклад виклику:
SELECT divide_numbers(10, 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: логування попереджень
Не завжди потрібно генерувати виключення для користувача. Іноді достатньо зареєструвати попередження. Зробимо функцію для перевірки віку студента:
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. - Повідомлення в консолі про те, що вік студента менше 18.
Логування і обробка виключень
Давай обʼєднаємо запис помилок і обробку виключень у більш складній функції. Уяви, що у нас є задача перерахувати оцінки студентів у таблиці grades. Якщо процес не виконано для одного зі студентів, помилка логуватиметься, але операція продовжиться.
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();
Такий підхід робить твою функцію набагато стійкішою, бо помилки окремо логуються і не зупиняють виконання для всіх даних.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ