Представьте ситуацию, где вы работаете с базой данных, обрабатываете кучу транзакций, но что-то пошло не так. Пользователь жалуется, бизнес кричит, а вы даже не видите, что конкретно произошло. Вот тут-то и вступает в игру правильное логирование ошибок. Оно позволяет:
- Сохранять ошибки в базе данных для последующего анализа.
- Легко восстанавливать детали ошибки: время её появления, сообщение и связанные данные.
- Улучшать ваш код, работая над типичными ошибками, которые можно обнаружить при анализе логов.
Короче, логирование — это как запись с камеры наблюдения для вашей базы данных: видите всё, что случилось не так, и когда это произошло.
Создаём таблицу error_log
Давайте начнём, как говорится, с фундамента. Нам нужно создать таблицу, в которой мы будем хранить информацию об ошибках. Таблица будет иметь следующие поля:
id— уникальный идентификатор ошибки.error_message— текст ошибки.error_time— время, когда произошла ошибка.- (Дополнительно)
context— контекст ошибки, если нам нужно хранить подробности.
CREATE TABLE error_log (
id SERIAL PRIMARY KEY, -- Уникальный идентификатор ошибки
error_message TEXT NOT NULL, -- Текстовое сообщение об ошибке
error_time TIMESTAMP NOT NULL DEFAULT NOW(), -- Время возникновения ошибки
context JSONB -- Дополнительные данные об ошибке
);
Что здесь происходит?
id SERIAL PRIMARY KEY— это поле создаёт уникальный идентификатор для каждой записи автоматически.error_message TEXT NOT NULL— здесь будет храниться текст ошибки. Поле обязательно для заполнения.error_time TIMESTAMP NOT NULL DEFAULT NOW()— поле, фиксирующее время события. Если вы не укажете значение, оно автоматически запишет текущее время благодаряDEFAULT NOW().context JSONB— опционально, для хранения дополнительной информации, например, данных об операции, где произошла ошибка.
После выполнения команды CREATE TABLE в вашей базе данных запишется структура для хранения логов.
Пример записи ошибок в таблицу
Теперь, когда таблица создана, займёмся практической частью: будем сохранять информацию об ошибках в таблицу. Мы напишем функцию, которая записывает текст ошибки и её время в error_log.
Пример функции для записи ошибок
CREATE OR REPLACE FUNCTION log_error(p_error_message TEXT, p_context JSONB DEFAULT NULL)
RETURNS VOID AS $$
BEGIN
INSERT INTO error_log (error_message, context)
VALUES (p_error_message, p_context);
END;
$$ LANGUAGE plpgsql;
p_error_message— входной параметр, куда передаётся текст ошибки.p_context— опциональный параметр для передачи дополнительных данных. По умолчанию принимает значениеNULL.INSERT INTO error_log (error_message, context)— добавляет новую запись в таблицуerror_log.DEFAULT NULL— если контекст не задан, поле заполняетсяNULL.
Теперь, если нужно записать сообщение об ошибке, вы можете вызвать функцию так:
SELECT log_error('Ошибка при выполнении запроса', '{"query": "SELECT * FROM data"}');
Это добавит новую запись в таблицу error_log.
Автоматическое логирование ошибок
Писать каждый раз SELECT log_error(...) вручную может быть неудобно. Давайте автоматизируем этот процесс с использованием обработки исключений.
Пример функции с обработкой исключений.
CREATE OR REPLACE FUNCTION divide_numbers(a NUMERIC, b NUMERIC)
RETURNS NUMERIC AS $$
DECLARE
result NUMERIC;
BEGIN
-- Попробуем выполнить деление
result := a / b;
-- Возвращаем результат
RETURN result;
EXCEPTION
WHEN division_by_zero THEN
-- Логируем ошибку и передаём контекст
PERFORM log_error('Деление на ноль', jsonb_build_object('a', a, 'b', b));
-- Генерируем исключение для пользователя
RAISE EXCEPTION 'Деление % на % невозможно — делитель равен нулю', a, b;
END;
$$ LANGUAGE plpgsql;
- Проверка деления: выполняется
result := a / b. Еслиb = 0, PostgreSQL выбрасывает исключениеdivision_by_zero. - Обработчик исключений (
EXCEPTION) перехватывает ошибку. - Внутри обработчика мы вызываем функцию
log_error, чтобы записать ошибку в таблицу вместе с параметрами, которые вызвали проблему. - После записи ошибки с помощью
RAISE EXCEPTIONвызывается новое сообщение об ошибке, чтобы пользователь также получил уведомление.
Пример вызова:
SELECT divide_numbers(10, 0);
Результат:
- Пользователь увидит ошибку:
Деление 10 на 0 невозможно — делитель равен нулю. - В таблице
error_logпоявится запись о делении на ноль.
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ