Сьогодні поговоримо про логування помилок у FastAPI, зачепимо і важливість логування, і практичні аспекти його реалізації. Логування — це ваш перший друг (а іноді й єдиний) у світі пошуку проблем в коді. Уявіть, що ваш додаток — це складна машина, а логи — її бортовий журнал. Коли щось іде не так, перше місце, куди ви дивитесь — саме туди.
Уявіть, що ви керуєте сайтом, який раптово перестав працювати. Без логів ви, швидше за все, почнете гадати як детектив без доказів: "Може, сервер перегрівся? Або база даних впала? А може хтось вставив у форму нестандартний емодзі?". Логування позбавляє від домислів і дає реальну інформацію.
Переваги логування:
- Діагностика помилок у реальному часі: можна швидко визначити, що пішло не так і де саме.
- Аналіз роботи додатка: навіть якщо помилок немає, логи допомагають зрозуміти, як поводиться додаток під навантаженням і прогнозувати проблеми.
- Аудит і безпека: у логах можна фіксувати підозрілі дії, наприклад, спроби зламу системи.
- Зручність відладки: коли щось ламається, логи дають розробнику "машину часу", щоб "відмотати" події назад.
Як працює логування у 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.
Типові помилки при логуванні
- Зайві логи: логування кожної дії, включно з несуттєвими, може створити "шум", через який складно буде знайти важливі записи.
- Відсутність контексту: логи без контекстної інформації (наприклад, ID користувача або час) втрачають свою цінність.
- Ігнорування рівнів логування: використання одного рівня для всіх повідомлень ускладнює фільтрацію.
- Логи без формату: нефомартовані логи виглядають як купа невпорядкованих повідомлень.
Тепер у вас є все необхідне, щоб ефективно логувати помилки і не тільки у FastAPI! Ви познайомилися з базовими і просунутими техніками логування, які допоможуть краще розуміти, що відбувається у вашому додатку. Готові до наступного кроку?
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ