fix(logging): defensive trace_id seed + falsy error/payload in CotJsonFormatter
log_requests middleware (BaseHTTPMiddleware) showed no-trace
because anyio.create_task_group() in Starlette 0.50.0 does not
always propagate ContextVar from raw ASGI TraceContextMiddleware.
Fix 1: defensive get_trace_id() check at log_requests entry —
if empty, seed_trace_id() to ensure every request has one.
Fix 2: CotJsonFormatter used 'if error:' and 'if payload:' which
silently drop empty strings (str(e)='') and empty dicts.
Changed to 'is not None' checks — preserves all data.
Root cause: 12 EXPLORE-without-error entries from belief_scope's
exception handler where Exception() has empty message.
This commit is contained in:
@@ -58,7 +58,7 @@ from .api.routes import (
|
||||
)
|
||||
from .api.routes.validation_tasks import router as validation_tasks
|
||||
from .core.auth.security import get_password_hash
|
||||
from .core.cot_logger import seed_trace_id
|
||||
from .core.cot_logger import get_trace_id, seed_trace_id
|
||||
from .core.database import AuthSessionLocal, init_db
|
||||
from .core.encryption_key import ensure_encryption_key
|
||||
from .core.logger import belief_scope, logger
|
||||
@@ -338,6 +338,12 @@ async def network_error_handler(request: Request, exc: NetworkError):
|
||||
# @POST Logs request and response details.
|
||||
@app.middleware("http")
|
||||
async def log_requests(request: Request, call_next):
|
||||
# Defensive trace_id seeding: TraceContextMiddleware (raw ASGI) seeds
|
||||
# trace_id in the root context, but BaseHTTPMiddleware may run in a
|
||||
# child task that doesn't inherit the ContextVar (Starlette 0.50.0 +
|
||||
# anyio.create_task_group). If empty, seed now.
|
||||
if not get_trace_id():
|
||||
seed_trace_id()
|
||||
with belief_scope("log_requests"):
|
||||
# Avoid spamming logs for polling endpoints
|
||||
is_polling = request.url.path.endswith("/api/tasks") and request.method == "GET"
|
||||
|
||||
@@ -103,9 +103,9 @@ class CotJsonFormatter(logging.Formatter):
|
||||
span_id = _span_id.get()
|
||||
if span_id:
|
||||
log_obj["span_id"] = span_id
|
||||
if payload:
|
||||
if payload is not None:
|
||||
log_obj["payload"] = payload
|
||||
if error:
|
||||
if error is not None:
|
||||
log_obj["error"] = error
|
||||
|
||||
return json.dumps(log_obj, ensure_ascii=False, default=str)
|
||||
|
||||
Reference in New Issue
Block a user