✨ Collect WebSocket connections opened before init() - #4938
Draft
bdibon wants to merge 6 commits into
Draft
Conversation
Bundles Sizes Evolution
|
🎉 All green!🧪 All tests passed 🎯 Code Coverage (details) 🔗 Commit SHA: fa87695 | Docs | Datadog PR Page | Give us feedback! |
Lift the fake WebSocket inlined in the WebSocket observable spec into a shared core test helper, so upcoming WebSocket tickets drive instrumentation through one test double instead of growing their own copies. The utility follows the XHR mock's shape: it swaps the `WebSocket` global and registers its own cleanup, including resetting the observable singleton. The observable spec's test cases and assertions are unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
WebSocket joins fetch, XHR, console and runtime errors as a buffered data source, so a socket constructed before init() is recorded rather than lost. The subscription is unconditional: the WebSocket opt-in is only known at init(), and consulting it before instrumenting would miss every connection opened before then — which is the point of the feature. The opt-in is left to the consumer of the source. The full WebSocketContext union crosses the buffer unchanged, with no coalescing in the core buffering layer. Nothing consumes the new source yet, so there is no customer-visible change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
WebSocket collection now consumes the buffered data observable instead of subscribing to the WebSocket observable directly, so connections opened before init() are replayed and reported as complete resource events. The opt-in gate (trackResources plus betaTrackWebSockets or the TRACK_WEBSOCKETS experimental feature) moves into the collection entry point, since it is not knowable until init(). RUM startup now calls it unconditionally; the returned stop handle is a no-op when the gate is closed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The collection layer only observes what webSocketObservable emits, so asserting that application handlers and payloads survive belongs with the instrumentation. Handler passthrough was already covered there; add the missing send-payload case and drop the collection-level duplicate. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
bdibon
force-pushed
the
boris.dibon/websocket-early-collection
branch
from
August 11, 2026 16:31
0386e91 to
fa87695
Compare
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.
Motivation
WebSocket collection subscribed to
initWebSocketObservable()only onceinit()had run, so anyconnection opened before then was invisible to RUM. A socket that a page opens at load — the common
case for chat, live feeds and collaborative editors — was never reported.
Changes
startBufferingData()gains aBufferedDataType.WEB_SOCKETsource, alongside runtime errors,fetch, XHR and console. Instrumentation now starts at SDK load rather than at
init().init(), andconsulting it before instrumenting would miss exactly the connections this change is about.
webSocketCollectionconsumesbufferedDataObservableinstead of the rawObservable<WebSocketContext>, so pre-init()activity is replayed on unbuffer.trackResourcesplusbetaTrackWebSocketsor theTRACK_WEBSOCKETSexperimental feature) moves from
startRumintostartWebSocketCollection.startRumcalls itunconditionally; when the gate is closed nothing is subscribed or allocated and the returned
stopis a no-op.startBufferingData()now takes an optional list ofBufferedDataTypesources to instrument.makeLogsPublicApipasses onlyRUNTIME_ERROR,FETCH,XHRandCONSOLE— logs neverinstruments
WebSocketor pays its buffering cost.makeRumPublicApipasses all five sources,including
WEB_SOCKET.WebSocketinlined in the observable spec becomes a shared core test utility(
mockWebSocket), and the collection specs now drive through instrumentation rather than pushingsynthetic contexts into an observable.
Known issues to resolve before merge
Two problems were surfaced while building this and deliberately deferred — each gets its own PR:
BufferedObservableis a 500-entry ring shared with runtime errors, fetch, XHR and console.
message-in/message-outfire per message, so one chatty socket over a seconds-long pre-init()window canevict every other buffered entry. Nothing is coalesced here; the existing
'Early data collection dropped data on unbuffer'debug telemetry is the tripwire. Coalescing(and where the aggregation should live) is addressed in a distinct PR.
TestBuilder.withPreInitScript()method to delay the init phase and execute code during the pre-init window.Test instructions
yarn dev, then insandbox/index.htmlopen a WebSocket beforeDD_RUM.init()and enablethe opt-in on init:
ws.close()from the console).websocketresource event is sent with the connection's message counts and sizes —the pre-
init()connection is reported rather than dropped.betaTrackWebSocketsremoved and confirm no WebSocket resource event is sent.DD_LOGS.init()without RUM) never patches the globalWebSocket.Checklist