Repository navigation
feat(node): add request context to OpenTelemetry spans - #5314
Conversation
There was a problem hiding this comment.
PostHog Review · Standard · reviewed 78bf4b0: 1 Must fix, 1 Should fix.
This is the only automatic review while this pull request is a draft. Comment @posthog review to review new changes sooner. Otherwise, a review of the new changes runs when you mark it ready for review.
Coherence0 spec problems · 0 failing chokepoints · 2 spec gaps · 0 guard failures 44 components, 7 bullets (5 invariants, 2 requirements), 0 problems Chokepoints: none declared. Spec gaps: 2 entrances with outside or unknown trust and no traced control, and 10 more held by the adoption baseline
Close one with |
Node SDK compliance v2Commit: d1abe3a Capture v0 — acceptance✅ 28 passed / ❌ 3 non-passing / Drill-down: run artifacts → ❌ server_snapshot_payload_false — failed_assertionExpected native data result Code: Failed step: acceptance/public/evaluate-flags.feature:868 the public snapshot outcomes should have these semantics: ❌ server_snapshot_payload_zero — failed_assertionExpected native data result Code: Failed step: acceptance/public/evaluate-flags.feature:868 the public snapshot outcomes should have these semantics: ❌ server_snapshot_payload_null — failed_assertionExpected native data result Code: Failed step: acceptance/public/evaluate-flags.feature:868 the public snapshot outcomes should have these semantics: Harness exit: 1; report validation exit: 1. Capture v1 — acceptance✅ 28 passed / ❌ 3 non-passing / Drill-down: run artifacts → ❌ server_snapshot_payload_false — failed_assertionExpected native data result Code: Failed step: acceptance/public/evaluate-flags.feature:868 the public snapshot outcomes should have these semantics: ❌ server_snapshot_payload_zero — failed_assertionExpected native data result Code: Failed step: acceptance/public/evaluate-flags.feature:868 the public snapshot outcomes should have these semantics: ❌ server_snapshot_payload_null — failed_assertionExpected native data result Code: Failed step: acceptance/public/evaluate-flags.feature:868 the public snapshot outcomes should have these semantics: Harness exit: 1; report validation exit: 1. Capture v0 — migration✅ 55 passed / ❌ 1 non-passing / Drill-down: run artifacts → ❌ migration/yaml-parity-v1/remote-flags-v1.feature::Omitted GeoIP settings default to enabled in flag requests — failed_assertionFlags field differs Code: Failed step: migration/yaml-parity-v1/remote-flags-v1.feature:134 the first flags request field "geoip_disable" should equal JSON false Operation: Field: Expected: false Actual value: true Harness exit: 1; report validation exit: 1. Capture v1 — migration✅ 120 passed / ❌ 1 non-passing / Drill-down: run artifacts → ❌ migration/yaml-parity-v1/remote-flags-v1.feature::Omitted GeoIP settings default to enabled in flag requests — failed_assertionFlags field differs Code: Failed step: migration/yaml-parity-v1/remote-flags-v1.feature:134 the first flags request field "geoip_disable" should equal JSON false Operation: Field: Expected: false Actual value: true Harness exit: 1; report validation exit: 1. |
|
Note 🤖 Automated comment by QA Swarm — not written by a human Verdict: ✅ APPROVE (round 1 @ 9331ef4)Three review findings are fixed and their threads are resolved:
ChecksAll Node tests (1,192), types, build, and lint pass. A GC regression test and a real HTTP/Express run pass. An independent review of the fix found no remaining issue. Automated by QA Swarm. Human review is required. This comment does not approve a merge or release. |
|
🤖 Comment written by an AI assistant. I would keep the existing Node SDK context lifetime for this PR rather than aborting Express and NestJS context on response For independent background jobs, callers should start a fresh context and trace, passing the session ID explicitly if that association is wanted. Queue workers likewise need explicit propagation. The new Would you consider removing the automatic response-abort behavior from this PR? It changes existing |
marandaneto
left a comment
There was a problem hiding this comment.
Automated advisory code review.
9331ef4 to
92308a2
Compare
There was a problem hiding this comment.
PostHog Review · Standard · reviewed c19aa99: 1 Should fix.
This is review round 3 on this pull request. When findings keep coming back in the same code, the approach is usually the problem. Before the next fix, check whether a different design would remove the whole class of findings.
|
🤖 Automated reply from Paul's agent: Agreed, thank you. In c19aa99 the Express middleware and the NestJS interceptor no longer end the context on response I made the same change to the Next.js and Nuxt wrappers in #5315 (8bb9fe4), so all the JS server integrations behave the same way. |
|
🤖 Automated comment from Paul's agent: @hpouillot, a question across all SDKs, following your point above: when should backend request context end? Each backend SDK chooses this for itself today, so they do not agree:
Your argument for the JS choice: a promise or timer that the handler starts can still be part of the same user action. An independent background job should start a fresh context and pass IDs on purpose. Proposal: add one rule to the sdk-specs traces spec, "Auto-captured context attributes". Work that starts inside a request keeps that request's IDs. A detached job that needs its own IDs starts a fresh context. SDKs can offer an opt-in way to end a context early. Then line up the other SDKs with this rule, one PR per SDK. Does that rule match what you had in mind? |
|
@pauldambra I agree, we should really enforce the behaviour on the sdk-spec repo. By the way, now that I think about it, if we forward the session id to background context, we could artificially inflate session duration metrics if an event is sent, right ? Maybe, by default, we could forward the distinct_id and not the session_id (after requests ends) ? |
marandaneto
left a comment
There was a problem hiding this comment.
approving to unblock, codewise is ok, my finding was fixed, but theres an open discussion with @hpouillot
Generated-By: PostHog Desktop Task-Id: 5681be44-4986-48db-b140-1bda88f75f96
Generated-By: PostHog Desktop Task-Id: 5681be44-4986-48db-b140-1bda88f75f96
Generated-By: PostHog Desktop Task-Id: 5681be44-4986-48db-b140-1bda88f75f96
Generated-By: PostHog Desktop Task-Id: 5681be44-4986-48db-b140-1bda88f75f96
Generated-By: PostHog Desktop Task-Id: 5681be44-4986-48db-b140-1bda88f75f96
Generated-By: PostHog Desktop Task-Id: 5681be44-4986-48db-b140-1bda88f75f96
Generated-By: PostHog Desktop Task-Id: 5681be44-4986-48db-b140-1bda88f75f96
Generated-By: PostHog Desktop Task-Id: 5681be44-4986-48db-b140-1bda88f75f96
An app that uses require('posthog-node') and imports posthog-node/otel
loads two module copies. Each copy had its own listener registry, so the
incoming request span was not enriched. Keep one registry on globalThis.
Generated-By: PostHog Desktop
Task-Id: e73c8b25-9501-4f04-9fd8-e82415e1f9f9
Express and NestJS no longer end the request context when the response finishes or closes. A promise or timer that the handler starts can still belong to the same user action, so getContext(), captured events, and new spans keep the request IDs as before. Apps that want a hard end can still pass ContextOptions.signal. Generated-By: PostHog Desktop Task-Id: e73c8b25-9501-4f04-9fd8-e82415e1f9f9
c19aa99 to
d1abe3a
Compare
Error tracking smokeCommit: ENOENT: no such file or directory, stat '/home/runner/work/_temp/smoke-report/summary.json'. Inspect the CI run. |
[Robot] Prepared with PostHog Desktop on behalf of @pauldambra.
Problem
Developers need to find backend work from a browser session. OpenTelemetry spans currently omit the existing PostHog request IDs.
Part of sdk-specs#128, with the proposed contract in sdk-specs#129.
Changes
posthog-node/otelentrypoint. Its public API exportsPostHogContextSpanProcessor(posthog)andenrichCurrentSpan(posthog).enrichCurrentSpan(posthog)helper from the main entry without loading OpenTelemetry. It does nothing until a processor is registered.ContextOptions.signalfor apps that want a hard end to a context. Aborting it ends the context without cancelling the handler.fresh: truekeeps an explicit job scope independent from its parent.Note
Detached work after the Express response must start a new context with explicit IDs. API-only spans without public readable attributes are skipped.
The manual helper enriches spans; it does not add a request lifecycle to a framework. A sampler-dropped intermediate hides earlier server ancestors, which remain unchanged.
Validation: all 1,187 tests in 42 Node SDK files passed after the combined changes. Source and public type checks, build, lint, formatting, and API reference generation passed. Real Fastify, Hono, and NestJS tests cover concurrent requests, errors, incoming and child spans, and detached work. The NestJS tests cover both Express and Fastify adapters. Official HTTP and Express instrumentation verifies the earlier HTTP server span. The main helper also runs with all OpenTelemetry imports blocked.
Release info Sub-libraries affected
Libraries affected
Checklist
If releasing new changes
pnpm changesetto generate a changeset file🤖 Agent context
Autonomy: Human-driven (agent-assisted)
GPT-6 (Codex) wrote the integrations with PostHog Desktop. Independent SDK review by gpt-6-astra found and fixed exception snapshots and fresh-scope behavior. Used Git, pnpm, Vitest, TypeScript, Oxlint, Oxfmt, API Extractor, and the GitHub CLI. A human must review this change.
The processor uses public readable SDK attributes. OpenTelemetry stays optional, and the application retains its provider, exporter, sampling, and other processors. No live PostHog project or browser replay test was run.
Created with PostHog Desktop