πͺ΅ Logging & Debug¶
Good exception handling is only half the battle β logging is what keeps your team sane in production.
With APIException, unexpected errors donβt just return a nice JSON response;
theyβre also automatically logged in a clean, structured way.
β How It Works¶
Auto-logging:
from api_exception import register_exception_handlers
from fastapi import FastAPI
app = FastAPI()
register_exception_handlers(
app=app,
use_fallback_middleware=True
)
1οΈβ£ All handled APIExceptions are logged with: - HTTP status - Exception code - Message - Context & traceback
2οΈβ£ Unhandled exceptions (like DB errors, 3rd-party failures) are caught by the fallback middleware and: - Return a consistent JSON error response (ISE-500 by default) - The full traceback to your console or logging system
π Log Levels¶
APIException uses Pythonβs built-in logging levels.
Hereβs a quick guide:
Level | Numeric Value | When to use |
---|---|---|
DEBUG | 10 | Detailed internal information for debugging (dev mode only). |
INFO | 20 | High-level application flow information (startup, shutdown, major events). |
WARNING | 30 | Something unexpected happened but the app can still continue. |
ERROR | 40 | Serious issues where an operation failed (exceptions, DB errors, etc.). |
CRITICAL | 50 | Severe errors that may crash the application or require immediate attention. |
from api_exception import logger
logger.setLevel("DEBUG")
π§© Custom Log Fields¶
Custom log fields give you the flexibility to enrich your logs with business-specific context.
Instead of only seeing technical details (status code, path, traceback), you can include who the user was, which service made the call, or masked identifiers.
This is especially useful in microservice architectures or when debugging customer-facing issues, since your logs will carry both technical and business context together.
log_header_keys
:¶
Choose which request headers appear in logs:
from api_exception import register_exception_handlers
from fastapi import FastAPI
app = FastAPI()
register_exception_handlers(
app,
log_header_keys=("x-request-id", "x-user-id")
)
extra_log_fields
:¶
Sometimes you need more than just the basics in your logs.
With extra_log_fields
, you can inject custom metadata into every log record.
Example¶
from api_exception import register_exception_handlers
from fastapi import FastAPI, Request
app = FastAPI()
def my_extra_fields(request: Request, exc: Exception):
def mask(value: str, visible: int = 4) -> str:
"""Mask sensitive data (keep last `visible` chars)."""
if not value or not isinstance(value, str):
return value
if len(value) <= visible:
return "*" * len(value)
return "*" * (len(value) - visible) + value[-visible:]
return {
"user_id": request.headers.get("x-user-id", "anonymous"),
"custom_tag": "billing-service",
"has_exception": exc is not None,
# Masked sensitive fields
"authorization": mask(request.headers.get("authorization", "")),
"api_key": mask(request.query_params.get("api_key", "")),
}
register_exception_handlers(app, extra_log_fields=my_extra_fields)
π What this does¶
β’ Adds user_id to every log (or βanonymousβ if missing).
β’ Adds a service tag (billing-service) so logs are easy to filter.
β’ Marks if the log was attached to an exception.
β’ Masks sensitive fields like Authorization headers and API keys.
Important
Define them once you register register_exception_handlers
and you use across the routers.
This makes logs safer and more useful especially in multi-service environments.
Example log output¶
event=api_exception path=/user/1 method=GET http_status=404
user_id=12345 custom_tag=billing-service has_exception=True
authorization=********abcd api_key=*****f9d2
Example General Usage¶
from api_exception import logger
logger.debug("Debugging details: user_id=42, payload=...")
logger.info("Service started successfully on port 8000")
logger.warning("Slow query detected, taking longer than 2s")
logger.error("Failed to connect to Redis")
logger.critical("Database unreachable! Shutting down...")
π File Logging¶
By default, APIException logs are written to the console.
If you want to persist logs to a file, you can attach a file handler:
from api_exception import add_file_handler, logger
# Send all logs to api_exception.log with DEBUG level
add_file_handler("api_exception.log", level="DEBUG")
logger.info("Application started")
logger.warning("Something looks suspicious")
logger.error("An error occurred")
βοΈ Example Output¶
When an exception happened, youβll see logs like:
When something unexpected happens, youβll see logs like:
β‘ Tips¶
β Use FastAPIβs native logging module to pipe logs to your file, console, or external log aggregator (ELK, CloudWatch, etc.).
β Combine this with FastAPI middlewares or your own log formatter if you want structured JSON logs.
β For sensitive environments, make sure your logs do not expose user data.
π Next¶
βοΈ Want to see how fallback works?
Check out πͺ Fallback Middleware
βοΈ Need better Swagger docs?
Go to π Swagger Integration
βοΈ Havenβt defined custom codes yet?
Read ποΈ Custom Exception Codes