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 'Error logged: %', 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('Division by zero attempted!', 'divide_numbers');
        -- Генерируем исключение
        RAISE EXCEPTION 'Division by zero is not allowed.';
    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('Student name must be provided!', 'add_student');
        RAISE EXCEPTION 'Student name cannot be empty.';
    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('Student age is below 18.', 'check_age');
        RAISE NOTICE 'Warning: Student age is below 18.';
    END IF;

    RAISE NOTICE 'Age check passed.';
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 'Updated grades for student %', student.name;
        EXCEPTION WHEN OTHERS THEN
            -- Логируем ошибку и продолжаем выполнение
            PERFORM log_error('Failed to update grades for student ' || student.name, 'recalculate_grades');
        END;
    END LOOP;
END;
$$ LANGUAGE plpgsql;

Пример вызова:

SELECT recalculate_grades();

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

2
Задача
SQL SELF, 52 уровень, 3 лекция
Недоступна
Логирование ошибок в вычислениях
Логирование ошибок в вычислениях
Комментарии (1)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Ra Уровень 35 Student
20 августа 2025
Интересно, нет ли альтернативного синтаксиса, слишком многословный, это же Бейсик натуральный, смесь с Паскалем 😂 Сообщения об ошибках из каменного века - что вот тут случилось? 🤣 ERROR: syntax error at or near "begin" LINE 3: begin