feat(metrics): record requests and httpx durations as a histogram - #967
Closed
pauldambra wants to merge 1 commit into
Closed
pauldambra wants to merge 1 commit into
pauldambra wants to merge 1 commit into
Conversation
Add the `network` option to the `metrics` client config. When set, the SDK wraps `requests.Session.send` and, when httpx is installed, `httpx.Client.send` and `httpx.AsyncClient.send`, and records `http.client.request.duration` with `method`, `host`, templated `path` and `status_class` attributes. `name` accepts a string or a function, `attributes` accepts a function. The wrappers only observe: they call the original with the same arguments, return its result or re-raise its error, and never let a recording failure reach the caller. Each redirect hop that `requests` sends is folded into the outer request. The SDK's own sessions are marked so PostHog's uploads are not recorded, and the wrappers are removed on `shutdown()`. This mirrors `metrics.network` in posthog-js. Generated-By: PostHog Desktop Task-Id: 7cbd4d4f-b9f1-4246-ba2a-dbe574f78b08
Contributor
posthog-python Compliance ReportDate: 2026-09-17 14:24:01 UTC ✅ All Tests Passed!111/111 tests passed Capture_V1 Tests✅ 94/94 tests passed View Details
Feature_Flags Tests✅ 17/17 tests passed View Details
|
Member
Author
|
🤖 Closed by an agent on Paul's behalf. We decided this duplicates what OpenTelemetry instrumentation already provides for server-side HTTP clients, and PostHog accepts OTLP metrics directly. Closing rather than merging. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
🤖 This description was written by an agent on Paul's behalf.
💡 Motivation and Context
posthog-js can now record a duration histogram for every
fetchandXMLHttpRequestthe page makes (metrics.network, PostHog/posthog-js#4918). Python services have no equivalent, so a Django app that wants timings for the HTTP calls it makes has to wrap each call by hand.This adds the same option to the Python SDK:
When set, the SDK wraps
requests.Session.sendand, when httpx is installed,httpx.Client.sendandhttpx.AsyncClient.send. Every HTTP or HTTPS request is recorded as thehttp.client.request.durationhistogram (unitms) withmethod,host, templatedpathandstatus_classattributes.nameaccepts a string or a function of the request (returnNoneto skip it).attributesaccepts a function of the request and response whose result is merged over the defaults.Design points, kept the same as posthog-js where they apply:
requestssessions and its async httpx client, so PostHog's own uploads (including the metrics flush) are never recorded.requestssends each redirect hop throughSession.sendagain. A context variable folds those into the outer request, so a redirected call is recorded once with its final status.shutdown(). If another wrapper was layered on top of ours and it cannot be spliced out, ours stays in place as a pass-through.pathreplaces all-digit and uuid-like segments with:id. Ids with a prefix or suffix (order-123,38217.pdf) are kept, so return your ownpathfromattributesfor those routes.Nothing Django-specific is needed: the middleware already uses the global or configured client, so setting
metrics={"network": True}on that client times every outbound call a view makes.This is a public API addition (
metrics["network"]). The shape copies the agreed posthog-js option rather than a new design, but per CONTRIBUTING it should still get a maintainer nod before merge.💚 How did you test it?
posthog/test/test_network_metrics.py(37 tests, parametrized where the cases are a table): default attributes, status classes, failures recorded asmissingand re-raised, redirects recorded once, non-HTTP schemes skipped, the SDK's own sessions skipped, httpx sync and async,nameandattributesconfig, invalid config warning and falling back to defaults, install on construction and removal on shutdown, layered wrapper pass-through, module-levelposthog.setup().uv run ruff format --check .,uv run ruff check ., mypy with the baseline filter,python -W error -c "import posthog",make public_api_check, and the full suite (2795 passed, 15 skipped).📝 Checklist
If releasing new changes
sampo addto generate a changeset file (added.sampo/changesets/network-metrics.mdby hand,minor)🤖 Agent context
Autonomy: Human-driven (agent-assisted)
Written with PostHog Desktop (Claude Code). Paul asked for the Python SDK equivalent of the posthog-js network metrics wrapper. Decisions: patch
Session.sendrather thanHTTPAdapter.sendso plainrequests.get()and non-https mounts are covered; mark the SDK's own sessions instead of matching PostHog hosts by URL, as posthog-js ended up doing; use a context variable rather than a depth counter so the redirect guard works for both threads and asyncio tasks; keep the new module's helpers private so the only new public surface is thenetworkoption and the default metric name.Created with PostHog Desktop