Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 2 additions & 3 deletions app/config/logger.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,10 @@ def emit(self, record):
while frame and frame.f_code.co_filename == logging.__file__:
frame = frame.f_back
depth += 1
logger.opt(depth=depth, exception=record.exc_info).log(
bound_logger = logger.bind(correlation_id=corr_id)
bound_logger.opt(depth=depth, exception=record.exc_info).log(
level, record.getMessage()
)
if corr_id:
logger.bind(correlation_id=corr_id)


def correlation_id_filter(record):
Expand Down
24 changes: 19 additions & 5 deletions app/middleware/error_handling.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,20 @@
import traceback
from typing import Any

from fastapi import Request, status
from fastapi.exceptions import RequestValidationError
from fastapi.responses import JSONResponse
from loguru import logger

from app.error import DispatcherException, ErrorResponse
from app.middleware.correlation_id import correlation_id_ctx
from loguru import logger


def _format_traceback_with_request_id(exc: Exception, request_id: str) -> str:
"""Prefix each traceback line so line-based filters keep the full stacktrace."""
traceback_text = "".join(traceback.format_exception(type(exc), exc, exc.__traceback__))
prefix = f"request_id={request_id} | "
return "\n".join(f"{prefix}{line}" for line in traceback_text.splitlines())


def get_dispatcher_error_response(
Expand All @@ -20,22 +30,26 @@ def get_dispatcher_error_response(

async def dispatch_exception_handler(request: Request, exc: DispatcherException):

content = get_dispatcher_error_response(exc, correlation_id_ctx.get())
logger.exception(f"DispatcherException raised: {exc.message}")
request_id = correlation_id_ctx.get()
content = get_dispatcher_error_response(exc, request_id)
logger.error(f"DispatcherException raised: {exc.message}")
logger.error(_format_traceback_with_request_id(exc, request_id))
return JSONResponse(status_code=exc.http_status, content=content.dict())


async def generic_exception_handler(request: Request, exc: Exception):

# DO NOT expose internal exceptions to the client
request_id = correlation_id_ctx.get()
content = ErrorResponse(
error_code="INTERNAL_SERVER_ERROR",
message="An unexpected error occurred.",
details=None,
request_id=correlation_id_ctx.get(),
request_id=request_id,
)

logger.exception(f"GenericException raised: {exc}")
logger.error(f"GenericException raised: {exc}")
logger.error(_format_traceback_with_request_id(exc, request_id))
return JSONResponse(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR, content=content.dict()
)
Expand Down
Loading