Logger & Error Handling Implementation Summary
Quick Reference for New Modules
1. Logger Setup (One line per module)
from app.core.logging import get_logger
logger = get_logger(__name__)
Error Handling Patterns
Pattern 1: Simple Error with Context
try:
result = await operation()
except SpecificException as e:
logger.error(
"Operation failed",
extra={
"operation": "operation_name",
"error": str(e),
"error_type": type(e).__name__
},
exc_info=True
)
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Operation failed"
)
Pattern 2: Validation Error with Details
if not email or "@" not in email:
logger.warning(
"Validation failed",
extra={
"field": "email",
"value_provided": bool(email),
"validation": "email_format",
"user_id": user_id
}
)
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail="Invalid email format"
)
Pattern 3: Authentication Error
if not token:
logger.warning(
"Authentication failed",
extra={
"reason": "missing_token",
"endpoint": request.url.path,
"client_ip": request.client.host if request.client else None
}
)
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Missing authentication token"
)
Pattern 4: Permission Denied
if user.role not in required_roles:
logger.warning(
"Access denied",
extra={
"user_id": str(user.id),
"user_role": user.role,
"required_role": required_roles,
"resource": request.url.path
}
)
raise HTTPException(
status_code=status.HTTP_403_FORBIDDEN,
detail="Insufficient permissions"
)
Pattern 5: Database Error
try:
result = await collection.insert_one(data)
except PyMongoError as e:
logger.error(
"Database operation failed",
extra={
"operation": "insert_one",
"collection": "collection_name",
"error": str(e),
"error_type": type(e).__name__
},
exc_info=True
)
raise HTTPException(
status_code=status.HTTP_503_SERVICE_UNAVAILABLE,
detail="Database operation failed"
)
Pattern 6: Success with Context
logger.info(
"User login successful",
extra={
"user_id": user.id,
"username": user.username,
"method": "password",
"ip_address": request.client.host
}
)
Global Exception Handlers (in main.py)
from fastapi import FastAPI
from fastapi.exceptions import RequestValidationError
from pydantic import ValidationError
from jose import JWTError
from pymongo.errors import PyMongoError, ConnectionFailure, OperationFailure
@app.exception_handler(RequestValidationError)
async def validation_exception_handler(request: Request, exc: RequestValidationError):
errors = [
{
"field": " -> ".join(str(loc) for loc in error["loc"]),
"message": error["msg"],
"type": error["type"]
}
for error in exc.errors()
]
logger.warning(
"Validation error",
extra={
"path": request.url.path,
"method": request.method,
"error_count": len(errors),
"errors": errors
}
)
return JSONResponse(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
content={
"success": False,
"error": "Validation Error",
"errors": errors
}
)
@app.exception_handler(JWTError)
async def jwt_exception_handler(request: Request, exc: JWTError):
logger.warning(
"JWT authentication failed",
extra={
"path": request.url.path,
"error": str(exc),
"client_ip": request.client.host if request.client else None
}
)
return JSONResponse(
status_code=status.HTTP_401_UNAUTHORIZED,
content={
"success": False,
"error": "Unauthorized",
"detail": "Invalid or expired token"
}
)
@app.exception_handler(PyMongoError)
async def mongodb_exception_handler(request: Request, exc: PyMongoError):
logger.error(
"Database error",
extra={
"path": request.url.path,
"error": str(exc),
"error_type": type(exc).__name__
},
exc_info=True
)
if isinstance(exc, ConnectionFailure):
status_code = status.HTTP_503_SERVICE_UNAVAILABLE
detail = "Database connection failed"
elif isinstance(exc, OperationFailure):
status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
detail = "Database operation failed"
else:
status_code = status.HTTP_500_INTERNAL_SERVER_ERROR
detail = "Database error occurred"
return JSONResponse(
status_code=status_code,
content={
"success": False,
"error": "Database Error",
"detail": detail
}
)
@app.exception_handler(Exception)
async def general_exception_handler(request: Request, exc: Exception):
logger.error(
"Unhandled exception",
extra={
"method": request.method,
"path": request.url.path,
"error": str(exc),
"error_type": type(exc).__name__,
"client_ip": request.client.host if request.client else None
},
exc_info=True
)
return JSONResponse(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
content={
"success": False,
"error": "Internal Server Error",
"detail": "An unexpected error occurred"
}
)
Context Fields Reference
| Field |
Usage |
Examples |
user_id |
User identifier |
"usr_123" |
username |
Username |
"john_doe" |
email |
Email address |
"john@example.com" |
operation |
Action being performed |
"insert_user", "delete_account" |
error |
Error message |
str(exception) |
error_type |
Exception class |
"ValidationError", "ConnectionFailure" |
status_code |
HTTP status |
400, 401, 403, 500 |
path |
Request path |
"/api/users/login" |
method |
HTTP method |
"POST", "GET", "PUT" |
client_ip |
Client IP |
"192.168.1.1" |
reason |
Why it failed |
"invalid_password", "missing_token" |
field |
Field name (validation) |
"email", "password" |
required_role |
Expected role |
"admin", "super_admin" |
user_role |
User's actual role |
"user" |
collection |
MongoDB collection |
"system_users" |
Response Format Template
{
"success": True,
"data": {...},
"message": "Operation completed"
}
{
"success": False,
"error": "Error Type",
"detail": "Detailed message",
"errors": [...]
}
Implementation Checklist for New Module
Common HTTP Status Codes
| Code |
Meaning |
Logger Level |
Use Case |
| 400 |
Bad Request |
warning |
Invalid input data |
| 401 |
Unauthorized |
warning |
Missing/invalid token |
| 403 |
Forbidden |
warning |
Insufficient permissions |
| 404 |
Not Found |
info |
Resource doesn't exist |
| 422 |
Validation Error |
warning |
Invalid field values |
| 500 |
Server Error |
error |
Unexpected exception |
| 503 |
Service Unavailable |
error |
DB connection failed |
Quick Copy-Paste Template
"""
Module description.
"""
from fastapi import APIRouter, Depends, HTTPException, status
from fastapi.responses import JSONResponse
from app.core.logging import get_logger
from app.system_users.services.service import SystemUserService
from app.dependencies.auth import get_system_user_service
logger = get_logger(__name__)
router = APIRouter(prefix="/api/path", tags=["Category"])
@router.post("/endpoint")
async def endpoint_function(
data: RequestModel,
service: SystemUserService = Depends(get_system_user_service)
):
"""
Endpoint description.
Raises:
HTTPException: 400 - Invalid input
HTTPException: 401 - Unauthorized
HTTPException: 500 - Server error
"""
try:
if not data.field:
logger.warning(
"Validation failed",
extra={
"field": "field_name",
"reason": "empty_value"
}
)
raise HTTPException(
status_code=status.HTTP_422_UNPROCESSABLE_ENTITY,
detail="Field is required"
)
result = await service.operation(data)
logger.info(
"Operation successful",
extra={
"operation": "operation_name",
"result_id": result.id
}
)
return {
"success": True,
"data": result,
"message": "Operation completed"
}
except HTTPException:
raise
except Exception as e:
logger.error(
"Operation failed",
extra={
"operation": "operation_name",
"error": str(e),
"error_type": type(e).__name__
},
exc_info=True
)
raise HTTPException(
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
detail="Operation failed"
)