Repository navigation
feat(metrics): attach trace exemplars to flushed metric series - #1057
Conversation
A sample recorded inside a sampled span (PostHog start_span, or the current OpenTelemetry span) now stamps its series with an exemplar: trace ID, span ID, and value. One exemplar per series per flush window, last sample wins. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Generated-By: PostHog Desktop Task-Id: b5bf025d-b42c-461d-a1c9-1591be9ce76d
posthog-python Compliance ReportDate: 2026-10-13T22:58:00.115179+00:00 ✅ All Tests Passed!121/121 tests passed Capture_V1 Tests✅ 95/95 tests passed View Details
Capture_Ai Tests✅ 5/5 tests passed View Details
Feature_Flags Tests✅ 17/17 tests passed View Details
Feature_Flags_Local_Evaluation Tests✅ 4/4 tests passed View Details
|
|
[Medium impact] The PR appears safe to merge. Reviews (2) · Last reviewed commit: "test(metrics): cover exemplars from reco..." · Reviewed by Greptile |
The PostHog-span exemplar tests ran with tracing off, so start_span returned a pass-through span that echoes the parent traceparent. Run them on a client with traces enabled and assert the exemplar carries the recording span's own ID, not the parent's. Adds a nested-span case and keeps the unsampled PostHog span case on a recording span. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Generated-By: PostHog Desktop Task-Id: 6921bfc1-7e35-4637-83d4-353c11b3af05
marandaneto
left a comment
There was a problem hiding this comment.
Advisory review: one non-blocking suggestion; no blocking defects found.
marandaneto
left a comment
There was a problem hiding this comment.
The code looks good; no blocking defects found. One optional parser-reuse suggestion posted inline. Verdict: correct.
The exemplar path parsed the active span's traceparent with its own validator. It now calls parse_traceparent() from posthog/tracing and checks the normalized sampled flag, so one set of W3C rules covers both. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Generated-By: PostHog Desktop Task-Id: b5bf025d-b42c-461d-a1c9-1591be9ce76d
A new stamphog review started for this PR — the fresh verdict replaces this approval.
There was a problem hiding this comment.
Approved.
The ingestion and wire-contract changes have independent current-head assurance from PostHog Review (Standard). No unresolved substantive concerns or other refusal grounds were found; the older approvals are not counted as current-head assurance.
- PostHog Review (Standard) reviewed the current head: no findings.
- 👍 on the PR from greptile-apps[bot].
Gate mechanics and policy version
| Gate | Result | |
|---|---|---|
| prerequisites | ✓ | all clear |
| deny-list | ✓ | no deny categories matched |
| size | ✓ | 90L, 1F substantive, 226L/3F incl. docs/generated/snapshots — within ceiling |
| tier | ✓ | T1-agent / T1c-medium (226L, 3F, two-areas, feat) |
| stamphog 2.5.0 | .stamphog/policy.yml @ b51d807 · reviewed head b51d807 |
💡 Motivation and Context
client.metricsnever links to a trace, even when it is recorded inside a sampled span. The Metrics UI has no exemplar to jump from a chart point to the request that produced it.client.metrics, so the gap is in this SDK.Changes:
start_span) comes first. The current OpenTelemetry span is the fallback, imported lazily, soopentelemetry-apistays optional.traceId/spanId, nanos as a decimal string.💚 How did you test it?
test_metrics.pycover: a sampled OpenTelemetry span stamps every metric type with the last value; a PostHog span wins over an outer OpenTelemetry span; no span, an unsampled OpenTelemetry span, or an unsampled PostHog span give no exemplar; a failed flush keeps the exemplar for the retry.📝 Checklist
If releasing new changes
sampo addto generate a changeset file🤖 Agent context
Autonomy: Human-driven (agent-assisted)
claude-opus-5-5) through the PostHog Slack app. Skills:writing-pr-descriptions.Created with PostHog from a Slack thread
🤖 Generated with Claude Code