Skip to content

Instantly share code, notes, and snippets.

@n05tr0m0
Created March 13, 2024 20:43
Show Gist options
  • Save n05tr0m0/2693ed0e072eb1541a300093abedba53 to your computer and use it in GitHub Desktop.
Save n05tr0m0/2693ed0e072eb1541a300093abedba53 to your computer and use it in GitHub Desktop.
FastAPI + Uvicorn + loguru configuration
"""Classes and functions to customize logs."""
import logging
import sys
from loguru import logger
from app.config import AppSettings # originaly this is BaseSettings from Pydantic
class InterceptHandler(logging.Handler):
"""Logging handler interceptor from loguru documentation.
For more info see "https://loguru.readthedocs.io/en/stable/overview.html#entirely-compatible-with-standard-logging"
"""
def emit(self, record: logging.LogRecord) -> None:
"""Log the specified logging record by loguru logger."""
try:
# Get corresponding Loguru level if it exists
level = logger.level(record.levelname).name
except ValueError:
level = str(record.levelno)
# Find caller from where originated the logged message
frame, depth = logging.currentframe().f_back, 2
while frame.f_code.co_filename in (logging.__file__):
frame = frame.f_back
depth += 1
logger.opt(depth=depth, exception=record.exc_info).log(
level,
record.getMessage(),
)
def configure_logger(settings: AppSettings) -> None:
"""Add some loggers to loguru and config logging level."""
logging.getLogger().handlers = [InterceptHandler()]
logging.getLogger("uvicorn").setLevel(settings.LOG_LEVEL)
logging.getLogger("uvicorn.asgi").setLevel(settings.LOG_LEVEL)
logging.getLogger("uvicorn.error").setLevel(settings.LOG_LEVEL)
logging.getLogger("uvicorn.access").setLevel(settings.LOG_LEVEL)
logger.configure(
handlers=[
{
"sink": sys.stdout,
"level": settings.LOG_LEVEL,
"enqueue": True,
"format": (
"<green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green> | "
"<level>{level: <8}</level> | "
"<cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level> | "
"<yellow>{extra}</yellow>"
),
},
],
)
import uvicorn
from uvicorn.config import LOGGING_CONFIG
...
if __name__ == "__main__":
# NOTE: "app.logger.InterceptHandler" is the path to your logger's InterceptHandler class
LOGGING_CONFIG.update(
{
"loggers": {
"handlers": {"default": {"class": "app.logger.InterceptHandler"}}
}
}
)
uvicorn.run(
"app.main:app",
...
log_config=LOGGING_CONFIG,
)
# Log example:
# 2024-03-13 13:13:12.997 | INFO | uvicorn.main:run:522 - Will watch for changes in these directories: ['/webapp'] | {}
# 2024-03-13 13:13:12.998 | INFO | uvicorn.main:run:581 - Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit) | {}
# 2024-03-13 13:13:12.998 | INFO | uvicorn.supervisors.basereload:run:51 - Started reloader process [1] using StatReload | {}
# 2024-03-13 13:13:13.475 | INFO | asyncio.events:_run:84 - Started server process [9] | {}
# 2024-03-13 13:13:13.475 | INFO | uvicorn.server:startup:90 - Waiting for application startup. | {}
# 2024-03-13 13:13:13.478 | INFO | uvicorn.server:startup:90 - Application startup complete. | {}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment