mirror of
https://github.com/simonw/datasette.git
synced 2026-09-15 21:14:07 +02:00
Mark in-process datasette.client requests on their SERVER span
An internal datasette.client request runs the full ASGI stack, so it emits a second SERVER span nested inside the outer request's - which double-counts requests in any dashboard that counts by span kind. Rather than downgrading the inner span to INTERNAL (which would diverge from how httpx-ASGI instrumentation behaves and break the registry's kind-based dynamic-name matching), the span now carries an optional datasette.internal_client=True attribute for dashboards to filter on. The in_datasette_client ContextVar moves to telemetry.py so the middleware can read it without a circular import; its writers and the in_client() accessor stay in app.py. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012U7coQfVu8nK2R4q2mCULA
This commit is contained in:
parent
30f99d66c2
commit
fb89001ab1
6 changed files with 60 additions and 3 deletions
|
|
@ -856,3 +856,34 @@ def test_no_provider_takes_the_fast_path():
|
|||
)
|
||||
# Same fast path, other observable: nothing is stashed in the scope either.
|
||||
assert report["scope_keys"] == [False, False]
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_internal_client_requests_are_marked(ds, otel_spans):
|
||||
"""
|
||||
An in-process `datasette.client` request runs the full ASGI stack, so it
|
||||
emits its own SERVER span - `datasette.internal_client` marks those so
|
||||
kind-based dashboards can filter the double-count out. A request that
|
||||
arrives through the raw ASGI app (the shape of a real inbound request,
|
||||
without the DatasetteClient wrapper setting the ContextVar) must not
|
||||
carry the attribute.
|
||||
"""
|
||||
otel_spans.clear()
|
||||
assert (await ds.client.get("/")).status_code == 200
|
||||
server = _server_spans(otel_spans)
|
||||
assert server
|
||||
assert all(
|
||||
span.attributes.get("datasette.internal_client") is True for span in server
|
||||
)
|
||||
|
||||
import httpx2
|
||||
|
||||
transport = httpx2.ASGITransport(app=ds.app())
|
||||
async with httpx2.AsyncClient(
|
||||
transport=transport, base_url="http://localhost"
|
||||
) as client:
|
||||
otel_spans.clear()
|
||||
assert (await client.get("/")).status_code == 200
|
||||
server = _server_spans(otel_spans)
|
||||
assert server
|
||||
assert all("datasette.internal_client" not in span.attributes for span in server)
|
||||
|
|
|
|||
|
|
@ -93,6 +93,7 @@ EXPECTED_HTTP_ATTRIBUTES = {
|
|||
"user_agent.original",
|
||||
"http.response.status_code",
|
||||
"error.type",
|
||||
"datasette.internal_client",
|
||||
}
|
||||
|
||||
# The registry's own name for the request span is that template, not anything
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue