From 39791a6b2aa8566dd0ee31c8ed4a59951c7d78c9 Mon Sep 17 00:00:00 2001 From: David Reinhart Date: Wed, 2 Sep 2026 13:28:46 -0700 Subject: [PATCH] fix(logging): record the complete request path for variant endpoints PopulatedRawContextMiddleware took the request path from request.url.path. Starlette builds that URL by reassembling the scope into a string and re-parsing it, so urlsplit reads everything after the first '#' as a fragment. A variant URN is {score_set_urn}#{n}, which means the path in the canonical log line stopped at the score set: the variant number and the sub-resource were both dropped, and so was the query string, which sits after the '#' in the reassembled string. /variants/{urn}, /variants/{urn}/csv and /variants/{urn}/csv-namespaces logged one indistinguishable path, and no log line recorded which variant had been asked for. Read the path from the ASGI scope, which arrives percent-decoded and whole. ASGI guarantees 'path' on both http and websocket scopes, so this needs none of the defensiveness the method read below it carries, where only an http scope has the key. Nothing reads this context key. log_request serializes the context into the "Request completed." line and no behaviour depends on the value, so the change is confined to what the logs say. lib/slack.py interpolates request.url whole, and URL.__str__ returns the unparsed string, so the fragment survives there and it was never affected. --- src/mavedb/lib/logging/context.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/mavedb/lib/logging/context.py b/src/mavedb/lib/logging/context.py index 075efb586..ad3e84413 100644 --- a/src/mavedb/lib/logging/context.py +++ b/src/mavedb/lib/logging/context.py @@ -23,7 +23,10 @@ async def set_context(self, request: Union[Request, HTTPConnection]) -> dict: ctx: dict[str, Any] = {} ctx["request_ns"] = time.time_ns() - ctx["path"] = request.url.path + # The ASGI scope rather than request.url.path, which truncates at a '#'. Starlette builds + # request.url by re-parsing, so a variant URN's '#' opens a fragment there and the variant + # number and sub-resource are dropped: three /variants routes all logged the same path. + ctx["path"] = request.scope["path"] if isinstance(request, Request): ctx["method"] = request.method