Skip to content

Commit a9c7ccd

Browse files
committed
feat: improved logging with correlation ID
1 parent 802d8aa commit a9c7ccd

File tree

3 files changed

+54
-1
lines changed

3 files changed

+54
-1
lines changed

app/config/logger.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import sys
44

55

6+
from app.middleware.correlation_id import correlation_id_ctx
67
from loguru import logger
78

89

@@ -14,21 +15,34 @@ class InterceptHandler(logging.Handler):
1415
def emit(self, record):
1516
try:
1617
level = logger.level(record.levelname).name
18+
corr_id = correlation_id_ctx.get()
1719
except ValueError:
1820
level = record.levelno
21+
except LookupError:
22+
corr_id = None
23+
1924
frame, depth = logging.currentframe(), 2
2025
while frame and frame.f_code.co_filename == logging.__file__:
2126
frame = frame.f_back
2227
depth += 1
2328
logger.opt(depth=depth, exception=record.exc_info).log(
2429
level, record.getMessage()
2530
)
31+
if corr_id:
32+
logger.bind(correlation_id=corr_id)
33+
34+
35+
def correlation_id_filter(record):
36+
# Always inject the current correlation ID into the log record
37+
record["extra"]["correlation_id"] = correlation_id_ctx.get()
38+
return True
2639

2740

2841
def setup_logging():
2942
logger.remove() # remove default handler
3043
env = os.getenv("APP_ENV", "development")
3144

45+
logger.configure(extra={"correlation_id": None})
3246
if env == "production":
3347
# JSON logs for ELK
3448
logger.add(
@@ -37,6 +51,7 @@ def setup_logging():
3751
backtrace=False,
3852
diagnose=False,
3953
level="INFO",
54+
filter=correlation_id_filter,
4055
)
4156
else:
4257
# Pretty logs for dev
@@ -45,13 +60,15 @@ def setup_logging():
4560
colorize=True,
4661
format=(
4762
"<green>{time:YYYY-MM-DD HH:mm:ss}</green> | "
63+
"<i>{extra[correlation_id]}</i> | "
4864
"<level>{level: <8}</level> | "
49-
"<cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - "
65+
"<cyan>{name}</cyan>:<cyan>{function}</cyan> :<cyan>{line}</cyan> - "
5066
"{message}"
5167
),
5268
backtrace=True,
5369
diagnose=True,
5470
level="DEBUG",
71+
filter=correlation_id_filter,
5572
)
5673

5774
for name in (

app/main.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from fastapi import FastAPI
22

3+
from app.middleware.correlation_id import add_correlation_id
34
from app.platforms.dispatcher import load_processing_platforms
45
from .config.logger import setup_logging
56
from .config.settings import settings
@@ -15,6 +16,8 @@
1516
version="1.0.0",
1617
)
1718

19+
app.middleware("http")(add_correlation_id)
20+
1821
# Register Keycloak - must be done after FastAPI app creation
1922
# keycloak.register(app, prefix="/auth") # mounts OIDC endpoints for login if needed
2023

app/middleware/correlation_id.py

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import uuid
2+
import contextvars
3+
from fastapi import Request
4+
from loguru import logger
5+
6+
# Context var to store correlation ID per request
7+
correlation_id_ctx = contextvars.ContextVar("correlation_id", default=None)
8+
9+
10+
async def add_correlation_id(request: Request, call_next):
11+
# Try to reuse correlation ID from client headers, else generate one
12+
corr_id = request.headers.get("X-Correlation-ID", str(uuid.uuid4()))
13+
token = correlation_id_ctx.set(corr_id)
14+
15+
# Log at start
16+
logger.bind(correlation_id=corr_id).info(
17+
f"Incoming request: {request.method} - {request.url.path}",
18+
method=request.method,
19+
path=request.url.path,
20+
)
21+
22+
response = await call_next(request)
23+
24+
# Add correlation ID to response headers
25+
response.headers["X-Correlation-ID"] = corr_id
26+
27+
# Log at end
28+
logger.bind(correlation_id=corr_id).info(
29+
f"Request completed with status code {response.status_code}",
30+
status_code=response.status_code,
31+
)
32+
correlation_id_ctx.reset(token) # Reset after request
33+
return response

0 commit comments

Comments
 (0)