Skip to content

feat(rails): correlate request and child span context - #306

Open
pauldambra wants to merge 3 commits into
mainfrom
posthog/rails-span-context
Open

pauldambra wants to merge 3 commits into
mainfrom
posthog/rails-span-context

Conversation

@pauldambra

Copy link
Copy Markdown
Member

[Robot] Prepared with PostHog Desktop on behalf of @pauldambra.

💡 Motivation and Context

Rails developers need to connect browser sessions to backend request and child spans when they investigate a request.

Add the optional public PostHog::ContextSpanProcessor, loaded with posthog/otel/context_span_processor, to the application's existing OpenTelemetry provider. It copies sessionId and posthogDistinctId when each span starts. Request context also fills missing attributes on the already active request span. Explicit span attributes take precedence, and captured string IDs stay stable after context changes.

OpenTelemetry stays optional. The integration preserves the application's provider, exporter, sampling, and normal request handling. Setup and middleware order are documented in the Rails README. Thread, fiber, and job context transfer remains separate.

This follows the existing tracing header and span attribute contracts. Application-owned OpenTelemetry processor setup adds a public API tracked in sdk-specs#128.

💚 How did you test it?

  • Full RSpec suite with the default bundle and optional OpenTelemetry bundle: no failures. Default-bundle pending cases require optional OpenTelemetry gems.
  • Focused final integration tests: 18 examples, no failures with OpenTelemetry SDK 1.13.1 and real log correlation.
  • OpenTelemetry SDK 1.0.0 compatibility: request spans, child spans, precedence, isolation, snapshots, and fallback tests passed. Log forwarding needs its separate optional bundle.
  • Full RuboCop and public API snapshot checks passed.
  • Regression tests cover middleware order, concurrent requests, Rails fiber isolation, exception cleanup, empty IDs, absent tracing APIs, sampled-out spans, and loading without OpenTelemetry.

📝 Checklist

  • I reviewed the submitted code.
  • I added tests to verify the changes.
  • I updated the docs if needed.
  • No breaking change or entry added to the changelog.

If releasing new changes

  • Added a Changesets release entry for both packages.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Model: GPT-6 (Codex), through PostHog Desktop. Tools: Git, Ruby, RSpec, RuboCop, and GitHub CLI.

The change uses the existing Rails request context and stable OpenTelemetry processor hooks. The application registers the child span processor explicitly. Active Job propagation and the AI processor contract remain separate.


Created with PostHog Desktop

Generated-By: PostHog Desktop
Task-Id: 5681be44-4986-48db-b140-1bda88f75f96
@pauldambra
pauldambra requested a review from a team as a code owner October 16, 2026 18:31
@pauldambra pauldambra self-assigned this Oct 16, 2026
@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

posthog-ruby-async Compliance Report

Date: 2026-10-18T10:53:50.284110+00:00
Duration: 98376ms

⚠️ Some Tests Failed

46/47 tests passed, 1 failed


Capture Tests

✅ 30/30 tests passed

View Details
Test Status Duration
Format Validation.Event Has Required Fields ✅ 109ms
Format Validation.Event Has Uuid ✅ 105ms
Format Validation.Event Has Lib Properties ✅ 108ms
Format Validation.Distinct Id Is String ✅ 105ms
Format Validation.Token Is Present ✅ 106ms
Format Validation.Custom Properties Preserved ✅ 105ms
Format Validation.Event Has Timestamp ✅ 106ms
Format Validation.Non Utc Event Timestamp Is Converted To Utc ✅ 8ms
Retry Behavior.Retries On 503 ✅ 5310ms
Retry Behavior.Does Not Retry On 400 ✅ 2108ms
Retry Behavior.Does Not Retry On 401 ✅ 2108ms
Retry Behavior.Respects Retry After Header ✅ 8116ms
Retry Behavior.Implements Backoff ✅ 15620ms
Retry Behavior.Retries On 500 ✅ 5211ms
Retry Behavior.Retries On 502 ✅ 5211ms
Retry Behavior.Retries On 504 ✅ 5211ms
Retry Behavior.Max Retries Respected ✅ 15722ms
Deduplication.Generates Unique Uuids ✅ 111ms
Deduplication.Preserves Uuid On Retry ✅ 5212ms
Deduplication.Preserves Uuid And Timestamp On Retry ✅ 10313ms
Deduplication.Preserves Uuid And Timestamp On Batch Retry ✅ 5213ms
Deduplication.No Duplicate Events In Batch ✅ 112ms
Deduplication.Different Events Have Different Uuids ✅ 106ms
Compression.Sends Gzip When Enabled ✅ 105ms
Batch Format.Uses Proper Batch Structure ✅ 106ms
Batch Format.Flush With No Events Sends Nothing ✅ 5ms
Batch Format.Multiple Events Batched Together ✅ 108ms
Error Handling.Does Not Retry On 403 ✅ 2108ms
Error Handling.Does Not Retry On 413 ✅ 2108ms
Error Handling.Retries On 408 ✅ 5211ms

Feature_Flags Tests

⚠️ 16/17 tests passed, 1 failed

View Details
Test Status Duration
Request Payload.Request With Person Properties Device Id ✅ 108ms
Request Payload.Flags Request Uses V2 Query Param ✅ 106ms
Request Payload.Flags Request Hits Flags Path Not Decide ✅ 107ms
Request Payload.Flags Request Omits Authorization Header ✅ 107ms
Request Payload.Token In Flags Body Matches Init ✅ 106ms
Request Payload.Groups Round Trip ✅ 105ms
Request Payload.Groups Default To Empty Object ✅ 105ms
Request Payload.Disable Geoip False Propagates As Geoip Disable False ✅ 106ms
Request Payload.Disable Geoip Omitted Defaults To False ❌ 105ms
Request Payload.Flag Keys To Evaluate Contains Only Requested Key ✅ 106ms
Request Lifecycle.No Flags Request On Init Alone ✅ 2ms
Request Lifecycle.No Flags Request On Normal Capture ✅ 105ms
Request Lifecycle.Two Flag Calls Produce Two Remote Requests ✅ 111ms
Request Lifecycle.Mock Response Value Is Returned To Caller ✅ 106ms
Retry Behavior.Retries Flags On 502 ✅ 209ms
Retry Behavior.Retries Flags On 504 ✅ 249ms
Side Effect Events.Get Feature Flag Captures Feature Flag Called Event ✅ 109ms

Failures

request_payload.disable_geoip_omitted_defaults_to_false

Field 'geoip_disable' not found in /flags request body at path 'geoip_disable'. Available keys: ['distinct_id', 'groups', 'person_properties', 'group_properties', 'flag_keys_to_evaluate', 'token']

@github-actions

github-actions Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

posthog-ruby-sync Compliance Report

Date: 2026-10-18T10:53:38.174542+00:00
Duration: 93998ms

⚠️ Some Tests Failed

45/47 tests passed, 2 failed


Capture Tests

⚠️ 29/30 tests passed, 1 failed

View Details
Test Status Duration
Format Validation.Event Has Required Fields ✅ 10ms
Format Validation.Event Has Uuid ✅ 6ms
Format Validation.Event Has Lib Properties ✅ 6ms
Format Validation.Distinct Id Is String ✅ 10ms
Format Validation.Token Is Present ✅ 7ms
Format Validation.Custom Properties Preserved ✅ 7ms
Format Validation.Event Has Timestamp ✅ 6ms
Format Validation.Non Utc Event Timestamp Is Converted To Utc ✅ 7ms
Retry Behavior.Retries On 503 ✅ 5276ms
Retry Behavior.Does Not Retry On 400 ✅ 2010ms
Retry Behavior.Does Not Retry On 401 ✅ 2012ms
Retry Behavior.Respects Retry After Header ✅ 8016ms
Retry Behavior.Implements Backoff ✅ 15261ms
Retry Behavior.Retries On 500 ✅ 5111ms
Retry Behavior.Retries On 502 ✅ 5154ms
Retry Behavior.Retries On 504 ✅ 5116ms
Retry Behavior.Max Retries Respected ✅ 15577ms
Deduplication.Generates Unique Uuids ✅ 23ms
Deduplication.Preserves Uuid On Retry ✅ 5113ms
Deduplication.Preserves Uuid And Timestamp On Retry ✅ 10385ms
Deduplication.Preserves Uuid And Timestamp On Batch Retry ✅ 5121ms
Deduplication.No Duplicate Events In Batch ✅ 17ms
Deduplication.Different Events Have Different Uuids ✅ 8ms
Compression.Sends Gzip When Enabled ✅ 5ms
Batch Format.Uses Proper Batch Structure ✅ 6ms
Batch Format.Flush With No Events Sends Nothing ✅ 2ms
Batch Format.Multiple Events Batched Together ❌ 17ms
Error Handling.Does Not Retry On 403 ✅ 2008ms
Error Handling.Does Not Retry On 413 ✅ 2009ms
Error Handling.Retries On 408 ✅ 5114ms

Failures

batch_format.multiple_events_batched_together

Expected 1 requests, got 5

Feature_Flags Tests

⚠️ 16/17 tests passed, 1 failed

View Details
Test Status Duration
Request Payload.Request With Person Properties Device Id ✅ 8ms
Request Payload.Flags Request Uses V2 Query Param ✅ 6ms
Request Payload.Flags Request Hits Flags Path Not Decide ✅ 6ms
Request Payload.Flags Request Omits Authorization Header ✅ 9ms
Request Payload.Token In Flags Body Matches Init ✅ 7ms
Request Payload.Groups Round Trip ✅ 9ms
Request Payload.Groups Default To Empty Object ✅ 7ms
Request Payload.Disable Geoip False Propagates As Geoip Disable False ✅ 6ms
Request Payload.Disable Geoip Omitted Defaults To False ❌ 7ms
Request Payload.Flag Keys To Evaluate Contains Only Requested Key ✅ 6ms
Request Lifecycle.No Flags Request On Init Alone ✅ 2ms
Request Lifecycle.No Flags Request On Normal Capture ✅ 5ms
Request Lifecycle.Two Flag Calls Produce Two Remote Requests ✅ 10ms
Request Lifecycle.Mock Response Value Is Returned To Caller ✅ 6ms
Retry Behavior.Retries Flags On 502 ✅ 147ms
Retry Behavior.Retries Flags On 504 ✅ 142ms
Side Effect Events.Get Feature Flag Captures Feature Flag Called Event ✅ 8ms

Failures

request_payload.disable_geoip_omitted_defaults_to_false

Field 'geoip_disable' not found in /flags request body at path 'geoip_disable'. Available keys: ['distinct_id', 'groups', 'person_properties', 'group_properties', 'flag_keys_to_evaluate', 'token']

Generated-By: PostHog Desktop
Task-Id: 5681be44-4986-48db-b140-1bda88f75f96
@pauldambra

pauldambra commented Oct 17, 2026 •

Copy link
Copy Markdown
Member Author

Note

🤖 Automated comment by QA Swarm — not written by a human

Verdict: ✅ APPROVE (privacy round @ 5c2a70b)

Request context now keeps the Rails facade client that owned it at entry. A missing or disabled client adds no span IDs. A valid-key test-mode client stays enabled.

Checks

Default and OpenTelemetry suites pass. Public API is unchanged. An independent review accepted the change.

Earlier rounds

Verdict: ✅ APPROVE (round 1 @ 6732ece)

No remaining actionable finding in the reviewed change. Request and child span context, optional tracing support, and thread and fiber limits were checked.

Key findings

  • None remain in this revision. Existing focused tests and completed independent review coverage were reused.
  • Human review and the shared specification decision remain required. This comment does not approve a merge or release.

Convergence

No remaining duplicate finding.

Reviewer summaries

Reviewer Assessment
Router (gpt-6-sol) Complexity MEDIUM, confidence HIGH. Completed independent API and concurrency coverage was reused. No further delegation was needed.

Automated by QA Swarm. Human review is required. This comment does not approve a merge or release.

@pauldambra
pauldambra added this pull request to stack #309 October 17, 2026 20:40
Generated-By: PostHog Desktop
Task-Id: 5681be44-4986-48db-b140-1bda88f75f96

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant