JavaRush /Курси /SQL SELF /Автоматичне логування через функції

Автоматичне логування через функції

SQL SELF
Рівень 52 , Лекція 3
Відкрита

Уяви, що ти відправив свого бота у вільне плавання по базі даних, щоб він виконував складні операції. Рано чи пізно він спіткнеться, помилиться або натрапить на неочікувану ситуацію. Без логування він може просто замовкнути, а ти залишишся в здивуванні, що пішло не так. Автоматичне логування допомагає:

  • Відслідковувати виникнення помилок і попереджень.
  • Розуміти природу і причини збоїв.
  • Покращувати діагностику і продуктивність коду.

Завдяки автоматичному логуванню ти створюєш "чорну скриньку", яка фіксує події в базі даних і допомагає знаходити помилки, ніби ти топовий детектив.

Створення автоматичного логування через функції

  1. Визначаємо таблицю для логів

Щоб логувати помилки, нам потрібно місце для їх зберігання. Ми вже створили таблицю error_log у попередній лекції:

CREATE TABLE error_log (
    id SERIAL PRIMARY KEY,                 -- Унікальний ідентифікатор запису
    error_message TEXT NOT NULL,          -- Повідомлення про помилку
    error_time TIMESTAMP DEFAULT NOW(),   -- Час виникнення помилки
    function_name TEXT                    -- Ім'я функції, яка викликала помилку
);

Ця таблиця має все необхідне для запису помилок: текст помилки, її час і функцію, з якої вона виникла.

  1. Створюємо функцію для запису логів

Наступний крок — створити універсальну функцію, яка буде записувати помилки в таблицю 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;

Розберемо цей код:

  1. p_error_message і p_function_name — параметри функції, які приймають повідомлення про помилку і ім'я викликаної функції.
  2. INSERT INTO error_log додає запис у таблицю.
  3. 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();

Такий підхід робить твою функцію набагато стійкішою, бо помилки окремо логуються і не зупиняють виконання для всіх даних.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ