Давайте модернизируем наш интернет магазин, созданный в прошлом модуле, добавив в него и логгирование с помощью Middleware. Для этого откроем файл main.py и добавим следующий код:
from loguru import logger
logger.add("info.log", format="Log: [{extra[log_id]}:{time} - {level} - {message} ", level="INFO", enqueue = True)
Мы уже знаем, что его logger имеет метод add(), в котором мы можем регистрировать журналы.
Первый параметр — это обработчик, который решает, отправлять ли журналы в sys.stdout или в файл. В нашем приложении мы будем использовать файл info.log, содержащий все сообщения журнала.
Второй параметр — это формат логов, в котором мы можем создать собственный макет сообщений журнала, чтобы заменить его формат по умолчанию. Этот формат аналогичен строке Python без буквы "f", которая содержит заполнители, такие как {time}, {level}, {message} и любые пользовательские заполнители, которые необходимо заменить значением во время выполнения.
В файле info.log мы хотим, чтобы наши журналы начинались с ключевого слова Log, за которым сразу следовал специально созданный параметр log_id, а затем время регистрации, уровень и сообщение.
Важнейшей частью является тип уровня, который указывает степень детализации сообщений журнала, которые необходимо регистрировать. Если мы установим для параметра уровня значение INFO, это сообщит регистратору учитывать только те сообщения, которые имеют веса INFO, SUCCESS, WARNING, ERROR и CRITICAL. Регистратор будет обходить сообщения журнала за пределами этих уровней.
Возможности и функциональность Loguru могут многое. Например, мы можем создать дополнительные обработчики для генерации журналов, где каждый из этих обработчиков имеет разные типы хранения, ротации и представления. Кроме того, Loguru может позволить нам добавлять цвета в наши журналы с помощью некоторых цветовых разметок, таких как <red>, <blue> или <cyan>. Он также имеет декоратор @catch(), который можно применять для управления исключениями во время выполнения. Более подробнее вы можете посмотреть в документации.
Теперь когда мы подключили логирование, нам необходимо написать промежуточное ПО, для регистрации сообщений. В файле main.py, после строчки инициализации app = FastAPI() добавим следующий код:
from uuid import uuid4
from fastapi import FastAPI, Request
from fastapi.responses import JSONResponse
@app.middleware("http")
async def log_middleware(request: Request, call_next):
log_id = str(uuid4())
with logger.contextualize(log_id=log_id):
try:
response = await call_next(request)
if response.status_code in [401, 402, 403, 404]:
logger.warning(f"Request to {request.url.path} failed")
else:
logger.info('Successfully accessed ' + request.url.path)
except Exception as ex:
logger.error(f"Request to {request.url.path} failed: {ex}")
response = JSONResponse(content={"success": False}, status_code=500)
return response
В данном коде мы запускаем выполнение запроса, в случае если код ответа равен 401, 402, 403, 404, мы используем метод logger.warning(), в случае если код ответа другой, то используем метод logger.info(). В иных случаях мы используем метод logger.error() и вызываем 500 ошибку.
Запустим сервер и проверим работу. Откроем документацию, отправим несколько запросов и увидим в логах следующее:
Log: [350905b3-e12f-4521-92ec-86dfc2fec4cc:2024-05-04T17:13:08.310865+0300 - INFO - Successfully accessed /
Log: [5c5b2488-3436-42ce-a67d-3d4ec68d9471:2024-05-04T17:13:13.279273+0300 - INFO - Successfully accessed /docs
Log: [0ea9f41a-ec7e-46b1-b1ef-f84d5106c312:2024-05-04T17:13:13.338518+0300 - INFO - Successfully accessed /openapi.json
Log: [ffaf29ae-0ee2-4dec-a6b2-f167a4ca58d7:2024-05-04T17:13:17.595693+0300 - INFO - Successfully accessed /category/all_categories
Log: [a01fe29f-afbd-4350-93f3-f457daad6999:2024-05-04T17:13:25.484168+0300 - INFO - Successfully accessed /products/
Мы видим что логируем все запросы к нашему приложению. Попробуем перейти по не существующему адресу - http://127.0.0.1:8000/test, и мы увидим уже лог уровня WARNING.
Log: [28a101c0-7232-436a-86f1-0fdadd5a1f7e:2024-05-04T17:13:57.183846+0300 - WARNING - Request to /test failed
А в случае возникновения внутренней ошибки, в логах будет записываться уровень ERROR. Например, изменив конечную точку welcome:
@app.get("/")
async def welcome() -> dict:
raise Exception
То теперь при переходе на главную страницу сайта, в логах запишется следующая строка:
Log: [27295028-b626-4ff2-905a-6a8b0ea6acea:2024-05-04T17:19:56.841068+0300 - ERROR - Request to / failed:
В этом примере мы использовали FastAPI и Loguru для записи журнала логов и также использовали UUID для обозначения каждой записи в журнале. Это позволяет нам легко отслеживать логи каждого запроса и идентифицировать их источники.