JavaRush /Курсы /Модуль 4: FastAPI /Логирование ошибок в FastAPI

Логирование ошибок в FastAPI

Модуль 4: FastAPI
20 уровень , 4 лекция
Открыта

Сегодня мы поговорим о логировании ошибок в FastAPI, затронем как важность логирования, так и практические аспекты его реализации. Логирование — это ваш первый друг (и иногда единственный) в мире обнаружения проблем в коде. Представьте, что ваше приложение — это сложная машина, а логи — это её бортовой журнал. Когда что-то идёт не так, первым делом вы смотрите именно туда.

Представьте, что вы управляете сайтом, который внезапно перестаёт работать. Без логов вы, скорее всего, начнёте гадать, как детектив без улик: "Может, сервер перегрелся? Или база данных упала? А может, кто-то вставил в форму нестандартный эмодзи?". Логирование избавляет вас от догадок и предоставляет реальную информацию.

Преимущества логирования:

  1. Диагностика ошибок в реальном времени: вы можете быстро определить, что пошло не так, и где именно.
  2. Анализ работы приложения: даже если ошибки нет, логи помогают понять, как ведёт себя приложение под нагрузкой, и прогнозировать проблемы.
  3. Аудит и безопасность: в логах можно фиксировать подозрительные действия, например, попытки взлома системы.
  4. Лёгкость отладки: когда что-то ломается, логи дают разработчику "машину времени", чтобы "отмотать" события назад.

Как работает логирование в FastAPI?

FastAPI, как и любое Python-приложение, использует стандартный модуль logging из стандартной библиотеки Python. Этот модуль даёт множество возможностей для настройки логов: уровни важности сообщений, форматы вывода, хранение логов в файлах и отправка их на внешние сервисы.

Уровни логов

Python предоставляет несколько уровней логирования:

  • DEBUG: для детальной отладки приложения.
  • INFO: для общей информации о работе приложения.
  • WARNING: для предупреждений о возможных проблемах.
  • ERROR: для записи ошибок, которые не останавливают приложение.
  • CRITICAL: для записи ошибок, из-за которых приложение может перестать работать.

Пример: если сервер не смог обработать запрос из-за отсутствия данных в базе, это будет ERROR. Если пользователь ввёл некорректный email — это уже WARNING.


Настройка логирования в FastAPI

Шаг 1. Подключение стандартного модуля logging

FastAPI автоматически использует logging, но вы можете настроить его под свои нужды. Давайте начнём с простого примера:


import logging
from fastapi import FastAPI

# Настраиваем базовые параметры логирования
logging.basicConfig(
    level=logging.INFO,  # Уровень логирования
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",  # Формат логов
)

# Создаём объект FastAPI
app = FastAPI()

@app.get("/")
def read_root():
    logging.info("Root endpoint accessed")  # Логируем обращение
    return {"message": "Hello, FastAPI!"}

Здесь мы настроили базовые параметры логирования: уровень (INFO) и формат сообщения (timestamp - логгер - уровень - сообщение). Каждый раз, когда пользователь обращается к корневому эндпоинту, в логах появится соответствующее сообщение.

Шаг 2. Использование логгера

Хотя logging.basicConfig — это неплохое начало, в реальных проектах лучше использовать отдельные объекты логгеров. Это позволяет фильтровать логи для разных частей приложения.


# Создаём экземпляр логгера
logger = logging.getLogger("myapp")

@app.get("/items/{item_id}")
def read_item(item_id: int):
    if item_id < 1:
        logger.warning(f"Invalid item_id: {item_id}")  # Логируем предупреждение
        return {"error": "Invalid item_id"}
    
    logger.info(f"Accessed item with ID: {item_id}")  # Логируем общую информацию
    return {"item_id": item_id}

Теперь логгер будет писать сообщения только для эндпоинта /items. Это удобнее, чем один глобальный логгер для всего приложения.


Логирование ошибок через исключения

Когда в приложении происходит ошибка, логирование может помочь разработчику понять, что произошло. Рассмотрим пример:


@app.get("/divide")
def divide_numbers(a: int, b: int):
    try:
        result = a / b
    except ZeroDivisionError as e:
        logger.error("Division by zero attempted", exc_info=True)  # Логируем с деталями
        return {"error": "Cannot divide by zero"}
    return {"result": result}

Ключевой параметр здесь — exc_info=True. Он добавляет подробную трассировку ошибок (stack trace) в лог, что позволит вам точно определить место возникновения исключения.


Хранение логов в файлах

Логи в консоли — это здорово, но что делать, если ваша командная строка закроется, как только вы её забудете сохранить? Решение простое — записывать логи в файл.


# Конфигурация логирования с записью в файл
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
    handlers=[
        logging.FileHandler("myapp.log"),  # Запись в файл
        logging.StreamHandler()  # Дополнительно вывод в консоль
    ],
)

Теперь сообщения будут записываться в файл myapp.log, а также выводиться в консоль.


Интеграция логирования с внешними сервисами

Для больших приложений одного файла логов недостаточно. Например, если ваше приложение развернуто на нескольких серверах, лучше использовать централизованные системы логирования, такие, как Sentry, Logstash или Datadog.

Вот пример интеграции с Sentry:


pip install sentry-sdk[fastapi]

import sentry_sdk
from sentry_sdk.integrations.asgi import SentryAsgiMiddleware

# Инициализация Sentry
sentry_sdk.init(
    dsn="https://<your_sentry_dsn>",
    traces_sample_rate=1.0,
)

# Подключаем Middleware для интеграции с FastAPI
app.add_middleware(SentryAsgiMiddleware)

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


Полный пример логирования

Давайте соберём всё вместе: обработку исключений, запись логов в файл и вывод их в консоль.


from fastapi import FastAPI, HTTPException
import logging

# Настройка логирования
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s - %(name)s - %(levelname)s - %(message)s",
    handlers=[
        logging.FileHandler("myapp.log"),
        logging.StreamHandler(),
    ],
)

logger = logging.getLogger("myapp")

app = FastAPI()

@app.get("/")
def read_root():
    logger.info("Root endpoint accessed")
    return {"message": "Hello, world!"}

@app.get("/divide")
def divide(a: int, b: int):
    try:
        result = a / b
    except ZeroDivisionError as e:
        logger.error("Division by zero", exc_info=True)
        raise HTTPException(status_code=400, detail="Cannot divide by zero")
    return {"result": result}

Этот пример охватывает базовые техники логирования и обработки ошибок. Логи будут записаны как в консоль, так и в файл myapp.log.


Типичные ошибки при логировании

  1. Лишние логи: логирование каждого действия, включая несущественные, может создать "шум", из-за которого сложно будет найти важные записи.
  2. Отсутствие контекста: логи без контекстной информации (например, ID пользователя или времени) теряют свою ценность.
  3. Игнорирование уровней логирования: использование одного уровня для всех сообщений усложняет фильтрацию.
  4. Логи без формата: неформатированные логи выглядят как куча бессвязных сообщений.

Теперь у вас есть всё необходимое, чтобы эффективно логировать ошибки и не только в FastAPI! Вы познакомились с базовыми и продвинутыми техниками логирования, которые помогут вам лучше понимать происходящее в вашем приложении. Готовы к следующему шагу?

1
Задача
Модуль 4: FastAPI, 20 уровень, 4 лекция
Недоступна
Логирование с записью в файл
Логирование с записью в файл
1
Задача
Модуль 4: FastAPI, 20 уровень, 4 лекция
Недоступна
Кастомный логгер для обработки исключений
Кастомный логгер для обработки исключений
3
Опрос
Обработка ошибок и исключений, 20 уровень, 4 лекция
Недоступен
Обработка ошибок и исключений
Обработка ошибок и исключений
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ