⚗️ 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. |
🎉 All green!🧪 All tests passed 🎯 Code Coverage (details) 🔗 Commit SHA: 9bced06 | Docs | Datadog PR Page | Give us feedback! |
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".
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>
There was a problem hiding this comment.
💬 suggestion: what about moving this file and its tests to the resource directory?
There was a problem hiding this comment.
Should we move packages/browser-rum-core/src/domain/requestCollection.ts as well?
| startClocks: event.startClocks, | ||
| duration, | ||
| rawRumEvent, | ||
| domainContext: {}, |
There was a problem hiding this comment.
💭 thought: could it be worth providing the web socket instance in the domain context?
if yes, it could be added later down the road
There was a problem hiding this comment.
I don't see any value right now. What do you have in mind?
| const sessionExpiredSubscription = lifeCycle.subscribe(LifeCycleEventType.SESSION_EXPIRED, () => { | ||
| tracker.flushOpenConnections('session_end') | ||
| }) |
There was a problem hiding this comment.
❓ question: what do we want to do for websocket connection that spans across multiple sessions?
There was a problem hiding this comment.
For the prototype we'll just cut the tracking with the reason "session_end".
a67b7fb to
9e9b3ca
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9e9b3cad80
ℹ️ 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".
Run `yarn start` (nuxt preview) for both Nuxt test apps in Playwright so local runs match CI and avoid vite-node IPC flakes under parallel workers. Bind preview to fixed ports to avoid clashes with common 3000/3001 listeners. Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
9e9b3ca to
6ffe09f
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6ffe09f7cb
ℹ️ 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".
| const tracker = trackWebSocket(lifeCycle, initWebSocketObservable(), viewHistory, addDurationVital) | ||
|
|
||
| const sessionExpiredSubscription = lifeCycle.subscribe(LifeCycleEventType.SESSION_EXPIRED, () => { | ||
| tracker.flushOpenConnections('session_end') |
There was a problem hiding this comment.
Keep tracking open sockets after renewal
When a session expires while a WebSocket remains open, this call emits a session_end resource and flushOpenConnections clears the registry. The browser keeps using the same WebSocket instance, so after the session is renewed later message-* and close contexts have no new connecting event to re-register the socket and are ignored, causing long-lived sockets to stop producing websocket resources for the rest of the page. Keep or re-seed open connections across the session boundary so the renewed session can still be measured.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
We will address this in a future PR.

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".Important
This PR introduces a prototype for collecting WebSocket data, the model is not stable, some fields might be removed / renamed or added in a future PR.
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