✨ [RUM-16632] add browser profiling support#168
Conversation
|
@codex review |
There was a problem hiding this comment.
Stale comment
PR Review — Score: 4.3 / 5
This is a well-structured profiling feature: renderer profiles flow over the bridge,
ProfilingCollectionowns sampling and quota gating, and a dedicated multipart transport path delivers them separately from RUM. Unit and e2e coverage are strong, and ARCHITECTURE/README updates explain ownership boundaries clearly. I would approve after addressing the minor observability gap below.Why 4.3: Clear module boundaries, thorough tests (quota races, session renew, RUM
_dd.profilingenrichment, multipart upload), good docs, and sensible fail-open quota semantics with generation guards against stale results.Why not 5: A promise callback in
ProfilingCollectionskipsmonitor(), bridge profile payloads are unchecked before entering the pipeline, and the PROFILE transport track spins up even when profiling is disabled — small polish items on an otherwise solid change.
Findings
- [Minor] Missing
monitor()on quota callback — ThecheckProfilingQuotasettlement handler is not wrapped, so unexpected errors would bypass SDK telemetry.- [Minor] Unvalidated bridge profile payload — Malformed
profileIPC messages are cast and forwarded without shape checks.- [Nit] PROFILE transport always initialized — A third
BatchManagerstarts even whenprofilingSampleRateis 0.
Architectural flow
LoadingsequenceDiagram participant Renderer as Browser SDK (renderer) participant Bridge as RendererPipeline participant PC as ProfilingCollection participant Quota as Quota intake participant BM as Profile BatchManager participant DD as Datadog profile intake Renderer->>Bridge: IPC profile event Bridge->>PC: RawProfileEvent PC->>Quota: GET profiling/quota (sampled sessions) alt sampled and quota_ok PC->>BM: ServerProfileEvent BM->>DD: multipart POST (event + compressed trace) else not sampled or quota_ko PC--xBM: drop profile end PC->>Bridge: registerRum hook enriches _dd.profiling on renderer RUMBefore: Renderer profiles were not collected or transported by the Electron SDK; RUM events had no electron-owned profiling quota context.
After: Profiles cross the bridge as
RAWevents, electron appliesprofilingSampleRateunder the session sample rate, checks backend quota (fail-open with a generation guard), enriches renderer RUM with_dd.profilingquota/sampled-out context, and uploads via a dedicated PROFILE track using one-file-per-profile multipart encoding.Sent by Cursor Automation: electron-sdk reviews
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ba3a165d95
ℹ️ 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".
|
@codex review |
There was a problem hiding this comment.
Stale comment
PR Review — Score: 4.8 / 5
Follow-up commits on head
876bb588address every item from the prior review: the quota-check continuation is wrapped inmonitor(), malformed bridge profile payloads are rejected early, and the PROFILE transport track is gated onprofilingSampleRate > 0. The profiling pipeline remains well-factored with strong unit/e2e coverage and clear ARCHITECTURE/README documentation. I would approve.Why 4.8: Prior polish items are resolved; module boundaries, quota generation guards, multipart transport, and test coverage (quota races, session renew, RUM
_dd.profilingenrichment) remain strong.Why not 5: Documented bridge limitations (global
profilescapability, no renderer stop signal onquota_ko) and optimistic profile forwarding while the quota check is in flight are intentional trade-offs/out-of-scope items, not defects — but they are the remaining gaps versus a perfect score.
Architectural flow
LoadingsequenceDiagram participant Renderer as Browser SDK (renderer) participant Bridge as RendererPipeline participant PC as ProfilingCollection participant Quota as Quota intake participant BM as Profile BatchManager participant DD as Datadog profile intake Renderer->>Bridge: IPC profile event Bridge->>Bridge: validate profile/trace shape Bridge->>PC: RawProfileEvent PC->>Quota: GET profiling/quota (sampled sessions) alt sampled and quota_ok PC->>BM: ServerProfileEvent BM->>DD: multipart POST (event + compressed trace) else not sampled or quota_ko PC--xBM: drop profile end PC->>Bridge: registerRum hook enriches _dd.profiling on renderer RUMBefore: Renderer profiles were not collected or transported by the Electron SDK; RUM events had no electron-owned profiling quota context.
After: Profiles cross the bridge as
RAWevents (with shape validation), electron appliesprofilingSampleRateunder the session sample rate, checks backend quota (fail-open with a generation guard), enriches renderer RUM with_dd.profilingquota/sampled-out context, and uploads via a dedicated PROFILE track (only when profiling is enabled) using one-file-per-profile multipart encoding.Sent by Cursor Automation: electron-sdk reviews
876bb58 to
31a15ba
Compare
|
Codex Review: Didn't find any major issues. Another round soon, please! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
677e306 to
2e1d30f
Compare
- bump the submodule to 31cc845, adding `quota_reason` to the profiling internal context and vitals/actions to the browser profiling schema - wire the profiling schema into generate-schema-types so profilingEvent.types.ts is regenerated alongside the rum and telemetry types
- accept { proxy?, subdomain? } instead of positional proxy string
- prepend subdomain to intake hostname; append &ddforwardSubdomain to proxy URL
- use '&' separator when trackType already contains query params
- update BatchManager call site to options form
- add PROFILE event track/format and Raw/ServerProfileEvent types - dispatch profile bridge events in RendererPipeline; exclude them from MainAssembly via StandardRawEvent - advertise the profiles capability over the config channel when profilingSampleRate > 0
015b233 to
565a4d8
Compare
|
@codex review |
There was a problem hiding this comment.
Stale comment
PR Review — Score: 4.8 / 5
Head
8377b38is a naming-only follow-up (processProfile,rawEvent/serverEvent); all functional feedback from prior rounds remains addressed. The profiling pipeline is well-factored — bridge validation, quota gating with generation guards, RUM_dd.profilingenrichment, and multipart PROFILE transport are covered by strong unit and e2e tests, with clear ARCHITECTURE/README documentation. I would approve.Why 4.8: Prior polish items (monitor-wrapped quota callback, early bridge payload validation, PROFILE track gating) are in place; module boundaries, fail-open quota semantics, and test coverage remain strong.
Why not 5: Documented bridge limitations (global
profilescapability, no renderer stop signal onquota_ko) and optimistic profile forwarding while the quota check is in flight are intentional trade-offs/out-of-scope items, not defects — but they are the remaining gaps versus a perfect score.
Architectural flow
LoadingsequenceDiagram participant Renderer as Browser SDK (renderer) participant Bridge as RendererPipeline participant PC as ProfilingCollection participant Quota as Quota intake participant BM as Profile BatchManager participant DD as Datadog profile intake Renderer->>Bridge: IPC profile event Bridge->>Bridge: validate profile/trace shape Bridge->>PC: RawProfileEvent PC->>Quota: GET profiling/quota (sampled sessions) alt sampled and quota_ok PC->>BM: ServerProfileEvent BM->>DD: multipart POST (event + compressed trace) else not sampled or quota_ko PC--xBM: drop profile end PC->>Bridge: registerRum hook enriches _dd.profiling on renderer RUMBefore: Renderer profiles were not collected or transported by the Electron SDK; RUM events had no electron-owned profiling quota context.
After: Profiles cross the bridge as
RAWevents (with shape validation), electron appliesprofilingSampleRateunder the session sample rate, checks backend quota (fail-open with a generation guard), enriches renderer RUM with_dd.profilingquota/sampled-out context, and uploads via a dedicated PROFILE track (only when profiling is enabled) using one-file-per-profile multipart encoding.Sent by Cursor Automation: electron-sdk reviews
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8377b38dd5
ℹ️ 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".
|
@codex review |
8377b38 to
d5bf709
Compare
There was a problem hiding this comment.
Stale comment
PR Review — Score: 4.9 / 5
Head
d5bf709completes the profiling feature end-to-end: renderer profiles flow over the bridge with shape validation,ProfilingCollectiongates on sampling and backend quota (with generation guards), capture-time session attribution prevents miscorrelation on async flushes, and a dedicated multipart PROFILE transport path delivers them separately from RUM. Unit and e2e coverage are thorough (quota races, session renew, sampled-out override, multipart upload), and README/ARCHITECTURE document ownership boundaries clearly. I would approve.Why 4.9: Prior polish items remain addressed (
monitor()-wrapped quota callback, early bridge payload validation, PROFILE track gating); the new capture-timegetTrackedSessionIdattribution closes the async-flush session-mismatch gap; disk-buffer eviction and customer-facing upload failure surfacing are sensible additions.Why not 5: Documented bridge limitations (global
profilescapability, no renderer stop signal onquota_ko) and optimistic profile forwarding while the quota check is in flight are intentional trade-offs/out-of-scope items, not defects — but they are the remaining gaps versus a perfect score.
Architectural flow
LoadingsequenceDiagram participant Renderer as Browser SDK (renderer) participant Bridge as RendererPipeline participant PC as ProfilingCollection participant Quota as Quota intake participant BM as Profile BatchManager participant DD as Datadog profile intake Renderer->>Bridge: IPC profile event Bridge->>Bridge: validate profile/trace shape Bridge->>PC: RawProfileEvent PC->>Quota: GET profiling/quota (sampled sessions) alt sampled and quota_ok PC->>PC: resolve session at capture time PC->>BM: ServerProfileEvent BM->>DD: multipart POST (event + compressed trace) else not sampled or quota_ko PC--xBM: drop profile end PC->>Bridge: registerRum hook enriches _dd.profiling on renderer RUMBefore: Renderer profiles were not collected or transported by the Electron SDK; RUM events had no electron-owned profiling quota context.
After: Profiles cross the bridge as
RAWevents (with shape validation), electron appliesprofilingSampleRateunder the session sample rate, checks backend quota (fail-open with a generation guard), attributes each profile to the session covering its capture time (not the current session at receipt), enriches renderer RUM with_dd.profilingquota/sampled-out context, and uploads via a dedicated PROFILE track (only when profiling is enabled) using one-file-per-profile multipart encoding.Sent by Cursor Automation: electron-sdk reviews
d5bf709 to
4741793
Compare
|
@codex review |
There was a problem hiding this comment.
Stale comment
PR Review — Score: 4.9 / 5
Head
4741793delivers profiling end-to-end: renderer profiles cross the bridge with shape validation,ProfilingCollectionapplies sampling and backend quota gating (with generation guards), capture-time session attribution prevents miscorrelation on async flushes, and a dedicated multipart PROFILE transport path delivers them separately from RUM. New e2e scenarios cover the happy path,quota_ko, and sampled-out sessions; README/ARCHITECTURE document setup constraints (Document-Policy, custom protocol). I would approve.Why 4.9: Prior polish items remain addressed (
monitor()-wrapped quota callback, early bridge payload validation, PROFILE track gating); quota intake client has thorough fail-open/timeout handling; capture-timegetTrackedSessionIdcloses the async-flush session-mismatch gap; unit and e2e coverage are strong.Why not 5: Documented bridge limitations (global
profilescapability, no renderer stop signal onquota_ko) and optimistic profile forwarding while the quota check is in flight are intentional trade-offs/out-of-scope items, not defects — but they are the remaining gaps versus a perfect score.
Architectural flow
LoadingsequenceDiagram participant Renderer as Browser SDK (renderer) participant Bridge as RendererPipeline participant PC as ProfilingCollection participant Quota as Quota intake participant BM as Profile BatchManager participant DD as Datadog profile intake Renderer->>Bridge: IPC profile event Bridge->>Bridge: validate profile/trace shape Bridge->>PC: RawProfileEvent PC->>Quota: GET profiling/quota (sampled sessions) alt sampled and quota_ok PC->>PC: resolve session at capture time PC->>BM: ServerProfileEvent BM->>DD: multipart POST (event + compressed trace) else not sampled or quota_ko PC--xBM: drop profile end PC->>Bridge: registerRum hook enriches _dd.profiling on renderer RUMBefore: Renderer profiles were not collected or transported by the Electron SDK; RUM events had no electron-owned profiling quota context.
After: Profiles cross the bridge as
RAWevents (with shape validation), electron appliesprofilingSampleRateunder the session sample rate, checks backend quota (fail-open with a generation guard), attributes each profile to the session covering its capture time (not the current session at receipt), enriches renderer RUM with_dd.profilingquota/sampled-out context, and uploads via a dedicated PROFILE track (only when profiling is enabled) using one-file-per-profile multipart encoding.Sent by Cursor Automation: electron-sdk reviews
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4741793e05
ℹ️ 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".
4741793 to
7f127ed
Compare
|
@codex review |
There was a problem hiding this comment.
Stale comment
PR Review — Score: 4.9 / 5
Head
7f127edbcompletes the profiling feature end-to-end: renderer profiles cross the bridge with shape validation,ProfilingCollectiongates on sampling and per-session backend quota (with capture-time session attribution), and a dedicated multipart PROFILE transport delivers them separately from RUM. The follow-up per-sessiondeniedSessionsmap closes the cross-renewal quota race Codex flagged; README/ARCHITECTURE document setup constraints (Document-Policy, custom protocol). I would approve.Why 4.9: All prior polish items remain addressed (
monitor()-wrapped quota callback, early bridge payload validation, PROFILE track gating); quota decisions are keyed by the capturing session id; capture-timegetTrackedSessionIdprevents async-flush miscorrelation; unit and e2e coverage are thorough (happy path,quota_ko, sampled-out, renewal edge cases).Why not 5: Documented bridge limitations (global
profilescapability, no renderer stop signal onquota_ko) and optimistic profile forwarding while the quota check is in flight are intentional trade-offs/out-of-scope items, not defects — but they are the remaining gaps versus a perfect score.
Architectural flow
LoadingsequenceDiagram participant Renderer as Browser SDK (renderer) participant Bridge as RendererPipeline participant PC as ProfilingCollection participant Quota as Quota intake participant BM as Profile BatchManager participant DD as Datadog profile intake Renderer->>Bridge: IPC profile event Bridge->>Bridge: validate profile/trace shape Bridge->>PC: RawProfileEvent PC->>Quota: GET profiling/quota (sampled sessions) alt sampled and session not denied PC->>PC: resolve session at capture time PC->>BM: ServerProfileEvent BM->>DD: multipart POST (event + compressed trace) else not sampled or session quota_ko PC--xBM: drop profile end PC->>Bridge: registerRum hook enriches _dd.profiling on renderer RUMBefore: Renderer profiles were not collected or transported by the Electron SDK; RUM events had no electron-owned profiling quota context.
After: Profiles cross the bridge as
RAWevents (with shape validation), electron appliesprofilingSampleRateunder the session sample rate, checks backend quota per session (fail-open with per-session denial map), attributes each profile to the session covering its capture time (not the current session at receipt), enriches renderer RUM with_dd.profilingquota/sampled-out context, and uploads via a dedicated PROFILE track (only when profiling is enabled) using one-file-per-profile multipart encoding.Sent by Cursor Automation: electron-sdk reviews
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7f127edb3a
ℹ️ 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".
7f127ed to
f09a0d7
Compare
|
@codex review |
There was a problem hiding this comment.
Stale comment
PR Review — Score: 4.9 / 5
Head
f09a0d7completes the profiling feature end-to-end: renderer profiles cross the bridge with shape validation,ProfilingCollectiongates on sampling and per-session backend quota (with capture-time session attribution for both profiles and RUM enrichment), and a dedicated multipart PROFILE transport delivers them separately from RUM. The latest commit adds clear customer-facing setup docs (Document-Policy, custom protocol); e2e scenarios cover the happy path,quota_ko, and sampled-out sessions. I would approve.Why 4.9: All prior polish items remain addressed (
monitor()-wrapped quota callback, early bridge payload validation, PROFILE track gating); quota decisions are keyed by the capturing session id;getTrackedSessionId(startTime)prevents async-flush and cross-renewal miscorrelation for both profiles and RUM_dd.profiling; unit and e2e coverage are thorough.Why not 5: Documented bridge limitations (global
profilescapability, no renderer stop signal onquota_ko) and optimistic profile forwarding while the quota check is in flight are intentional trade-offs/out-of-scope items, not defects — but they are the remaining gaps versus a perfect score.
Architectural flow
LoadingsequenceDiagram participant Renderer as Browser SDK (renderer) participant Bridge as RendererPipeline participant PC as ProfilingCollection participant Quota as Quota intake participant BM as Profile BatchManager participant DD as Datadog profile intake Renderer->>Bridge: IPC profile event Bridge->>Bridge: validate profile/trace shape Bridge->>PC: RawProfileEvent PC->>Quota: GET profiling/quota (sampled sessions) alt sampled and session not denied PC->>PC: resolve session at capture time PC->>BM: ServerProfileEvent BM->>DD: multipart POST (event + compressed trace) else not sampled or session quota_ko PC--xBM: drop profile end PC->>Bridge: registerRum hook enriches _dd.profiling on renderer RUMBefore: Renderer profiles were not collected or transported by the Electron SDK; RUM events had no electron-owned profiling quota context.
After: Profiles cross the bridge as
RAWevents (with shape validation), electron appliesprofilingSampleRateunder the session sample rate, checks backend quota per session (fail-open with per-session denial map), attributes each profile and RUM enrichment to the session covering the capture/event time, and uploads via a dedicated PROFILE track (only when profiling is enabled) using one-file-per-profile multipart encoding.Sent by Cursor Automation: electron-sdk reviews
|
Codex Review: Didn't find any major issues. You're on a roll. Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
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". |
- add ProfilingCollection to enrich renderer profile events into server events, gated by session sampling - add ProfileBatchProducer/Consumer/Manager for disk-buffered profile batching - wire ProfilingCollection into SDK init; register profile batching in Transport.create() and flush()
Bounds disk growth when uploads fail for a long time; evicts the oldest .log files beyond maxLogFiles (100) on each write. Eviction is silent by design.
- add checkProfilingQuota querying the profiling quota intake with fail-open timeout handling - gate ProfilingCollection forwarding on the quota decision, with a generation counter to discard results stale across session renewals - export computeIntakeUrlForTrack for the quota client
- upgrade browser-rum to local profiling-bridge-mode build via portal: - add Document-Policy: js-profiling header to HTTP server - add profilingSampleRate: 100 to bridge-window init - extend Intake to capture /api/v2/profile multipart requests - add generateLongTask() and triggerProfilingFlush() to BridgeWindowPage - copy dd-trace patch to e2e/app for standalone yarn install ✅ add E2E test for quota_ko blocking profiling bridge events - add quotaDecision field + setQuotaResponse/assertNoProfilingRequest to Intake - handle quota GET requests (ddforwardSubdomain=quota) in the fake intake server - add initialIntakeQuotaDecision fixture option to pre-configure quota before app launch, avoiding a race with the SDK's startup quota check - add sessionSampleRate/profilingSampleRate: 100 to base SDK config in helpers
- serve the renderer over an app:// scheme carrying the Document-Policy: js-profiling header so the main window can run the JS Self-Profiling API - enable profiling on the Electron SDK init (profilingSampleRate: 100) - add a "Generate Long Task" button running named-function work so profiler samples attribute to renderer.js
- add Renderer Profiling section to README (enable via main-process init + js-profiling Document Policy) and profilingSampleRate config row - add PROFILE format/track to ARCHITECTURE event kinds
f09a0d7 to
9cad598
Compare
There was a problem hiding this comment.
PR Review — Score: 4.9 / 5
Head 9cad598 completes the profiling feature end-to-end: renderer profiles cross the bridge with shape validation, ProfilingCollection gates on sampling and per-session backend quota (with capture-time session attribution for both profiles and RUM enrichment), and a dedicated multipart PROFILE transport delivers them separately from RUM. Customer-facing setup docs (Document-Policy, custom protocol), playground wiring, and e2e scenarios (happy path, quota_ko, sampled-out) round out the change. I would approve.
Why 4.9: All prior polish items remain addressed (monitor()-wrapped quota callback, early bridge payload validation, PROFILE track gating); quota decisions are keyed by the capturing session id; getTrackedSessionId(startTime) prevents async-flush and cross-renewal miscorrelation for both profiles and RUM _dd.profiling; disk-buffer eviction and customer-facing upload failure surfacing are sensible additions; unit and e2e coverage are thorough.
Why not 5: Documented bridge limitations (global profiles capability, no renderer stop signal on quota_ko) and optimistic profile forwarding while the quota check is in flight are intentional trade-offs/out-of-scope items, not defects — but they are the remaining gaps versus a perfect score.
Architectural flow
sequenceDiagram
participant Renderer as Browser SDK (renderer)
participant Bridge as RendererPipeline
participant PC as ProfilingCollection
participant Quota as Quota intake
participant BM as Profile BatchManager
participant DD as Datadog profile intake
Renderer->>Bridge: IPC profile event
Bridge->>Bridge: validate profile/trace shape
Bridge->>PC: RawProfileEvent
PC->>Quota: GET profiling/quota (sampled sessions)
alt sampled and session not denied
PC->>PC: resolve session at capture time
PC->>BM: ServerProfileEvent
BM->>DD: multipart POST (event + compressed trace)
else not sampled or session quota_ko
PC--xBM: drop profile
end
PC->>Bridge: registerRum hook enriches _dd.profiling on renderer RUM
Before: Renderer profiles were not collected or transported by the Electron SDK; RUM events had no electron-owned profiling quota context.
After: Profiles cross the bridge as RAW events (with shape validation), electron applies profilingSampleRate under the session sample rate, checks backend quota per session (fail-open with per-session denial map), attributes each profile to the session covering its capture time (not the current session at receipt), enriches renderer RUM with _dd.profiling quota/sampled-out context, and uploads via a dedicated PROFILE track (only when profiling is enabled) using one-file-per-profile multipart encoding.
Sent by Cursor Automation: electron-sdk reviews
thomasbertet
left a comment
There was a problem hiding this comment.
LGTM.
Added few suggestions, including being more explicit about the parts that are duplicated between Electron and Browser SDK.
- rename deniedSessions to quotaDeniedSessions to convey denial is quota-driven - note the pass-through SKIPPED keeps the renderer's own _dd.profiling
Reference the canonical profiling quota check spec that all SDK implementations follow, so the behavior stays aligned across SDKs rather than cross-referencing each other's source.


Motivation
Add browser profiling support to Electron apps.
Profiles are produced by the Browser SDK's profiler in the renderer and routed over the bridge to the Electron SDK (Browser SDK support added in 7.4.0).
This PR implements the Electron SDK side: collecting those profiles, gating them on sampling and a backend quota check, transporting them to Datadog, and enriching RUM events with the profiling context.
Changes
profilingSampleRateoption to control how many sessions are profiled (applied under the session sample rate).Test instructions
profilingSampleRate: 100and runyarn start._dd.profilingcontext.Checklist