⚗️ Collect WebSocket resource events#4718
Conversation
|
Warning This pull request is not mergeable via GitHub because a downstack PR is open. Once all requirements are satisfied, merge this PR as a stack on Graphite.
This stack of pull requests is managed by Graphite. Learn more about stacking. |
|
✨ Fix all issues with BitsAI or with Cursor
|
Bundles Sizes Evolution
|
f9f774b to
f67e33e
Compare
ec073a8 to
98e75c8
Compare
f67e33e to
adaf9f3
Compare
98e75c8 to
1afcc3a
Compare
adaf9f3 to
cb90533
Compare
1afcc3a to
9b239da
Compare
cb90533 to
7fc9595
Compare
7fc9595 to
770842b
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 770842b8f7
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| type: RumEventType.RESOURCE, | ||
| resource: { | ||
| id: generateUUID(), | ||
| type: ResourceType.WEBSOCKET, |
There was a problem hiding this comment.
Add schema support before emitting websocket resources
The generated RUM event schema still only allows the existing resource types (see packages/browser-rum-core/src/rumEvent.types.ts:665-676), and this PR also skips websocket resources in format validation (packages/browser-rum-core/test/formatValidation.ts:25-27). When track_web_sockets is enabled, events assembled here have resource.type: "websocket", so they do not match the SDK/intake schema and can be rejected or hidden by downstream validation. Please land/regenerate schema support before emitting this resource type.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
This is a prototype and the data collected is subject to change, downstream validation has been handled.
| 'send', | ||
| ({ target: instance, parameters: [data], onPostCall }) => { | ||
| const size = computePayloadSize(data) | ||
| const bufferedAmountPreSend = instance.bufferedAmount |
There was a problem hiding this comment.
Sample bufferedAmount after send
For a single large send, bufferedAmount is often 0 before the native send() call and only increases once that call queues the payload, so sampling only bufferedAmountPreSend makes buffered_amount_max miss the last/only write and under-report the real queue peak. Capture the value in the post-call path (or include both before and after) before updating the max.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Sampling bufferedAmountPreSend allows us to spot back pressure issues, if we instrument the post-call path, we'll just see the bufferedAmount grow by the size of the message that has just been sent.
|
|
||
| if (isExperimentalFeatureEnabled(ExperimentalFeature.TRACK_WEB_SOCKETS)) { | ||
| const webSocketCollection = startWebSocketCollection(lifeCycle, viewHistory, vitalCollection.addDurationVital) | ||
| cleanupTasks.push(webSocketCollection.stop) |
There was a problem hiding this comment.
Flush open websockets before stopping resources
When RUM is stopped while a socket is still open, cleanup tasks run in registration order: resourceCollection.stop() is registered earlier and stops its task queue before this later webSocketCollection.stop() emits WEBSOCKET_COMPLETED for open connections. That means the intended session_end resource is queued after the resource pipeline is already stopped and can be lost; flush websocket connections before stopping the resource collection/batch, or avoid emitting them during stop.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
In practice, the stop() cleanup isn't ran.
Also, the only way resourceCollection.stop() prevents a websocket event from being collected is when there's a corresponding task in taskQueue which is an unlikely window.
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
770842b to
4251e33
Compare
|
/to-staging |
|
View all feedbacks in Devflow UI.
Commit 4251e33ae1 will soon be integrated into staging-25.
Commit 4251e33ae1 has been merged into staging-25 in merge commit 07d8c51b44. If you need to revert this integration, you can use the following command: |
Integrated commit sha: 4251e33 Co-authored-by: bdibon <boris.dibon@datadoghq.com>

Motivation
WebSocket connections are a blind spot in RUM resource monitoring. This PR adds WebSocket tracking behind the
track_web_socketsexperimental feature flag, collecting per-connection metrics (message counts, sizes, timing, handshake duration, close codes) and reporting them asresourceevents withresource.type: "websocket".Changes
packages/browser-corewebSocketObservable: Instruments theWebSocketconstructor andsendmethod viainstrumentConstructor/instrumentMethodto emit lifecycle events:connecting,open,message-in,message-out,closed. Tracks payload sizes and clock timestamps for each event.ResourceType.WEBSOCKET = 'websocket'to the resource type enum.ExperimentalFeature.TRACK_WEB_SOCKETS = 'track_web_sockets'experimental flag.packages/browser-rum-corewebSocketCollection: ConsumeswebSocketObservableand maintains an in-memory registry of open connections. Aggregates metrics (message counts/sizes, first-message offsets, longest inbound silence, buffered amount peaks) and emitsWEBSOCKET_COMPLETEDlifecycle events on close or session expiry.resourceCollection: Subscribes toWEBSOCKET_COMPLETEDand assemblesRumResourceEventobjects withresource.type: "websocket"and aresource.websocketsub-object containing the full connection metrics.lifeCycle: Adds theWEBSOCKET_COMPLETEDevent type and its payload type.rawRumEvent.types: AddsWebSocketResourcePropertiesinterface and wires it intoRawRumResourceEvent.startRum: StartswebSocketCollectionwhen theTRACK_WEB_SOCKETSexperimental feature is enabled.scripts/dev-serverAccess-Control-Allow-Origin: *response header to support cross-origin WebSocket testing from the sandbox.Test instructions
Run unit tests:
Manual end-to-end test:
Open
http://localhost:8080and replacesandbox/index.htmlwith:Click the button, wait ~3 seconds, then open a new tab to flush events:
Expected output — a resource event with:
{ "resource": { "type": "websocket", "url": "ws://localhost:8765", "websocket": { "handshake_succeeded": true, "messages_in": { "count": 2, "size": 22 }, "messages_out": { "count": 2, "size": 22 }, "close_code": 1000, "was_clean": true, "tracking_end_reason": "close_event" } } }Checklist