Conversation
|
All contributors have signed the CLA ✍️ ✅ |
This stack of pull requests is managed by Graphite. Learn more about stacking. |
🎉 All green!❄️ No new flaky tests detected 🎯 Code Coverage (details) 🔗 Commit SHA: 8130067 | Docs | Datadog PR Page | Give us feedback! |
4df303f to
b539c14
Compare
Bundles Sizes Evolution
🚀 CPU Performance
🧠 Memory Performance
|
|
I have read the CLA Document and I hereby sign the CLA |
3042bfe to
98490f7
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 98490f775f
ℹ️ 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".
9bbedbf to
bf9a162
Compare
bf9a162 to
b9da4f3
Compare
b9da4f3 to
0040ee8
Compare
0040ee8 to
653aee9
Compare
a90f65e to
43903ba
Compare
Introduce the browser debugger SDK and probe execution pipeline so browser code can evaluate conditions, capture snapshots, and render probe messages at runtime. Add Delivery API polling plus sandbox and performance tooling to support probe delivery and testing.
43903ba to
7d1efe7
Compare
mormubis
left a comment
There was a problem hiding this comment.
I'd get another approval before merging. We usually look for at least two approvals.
| function toSdkSource(source: TransportConfiguration['source']): SdkSource { | ||
| return source === 'dd_debugger' ? 'browser' : source | ||
| } |
There was a problem hiding this comment.
❓ question: why setting the source to dd_debugger to set it back to browser later?
I think we should keep dd_debugger (or just debugger for consistency) and your team should maintain their own monitors, dashboards, ...
| DD_LOGS?: LogsGlobal | ||
| DD_RUM?: RumGlobal | ||
| DD_DEBUGGER?: DebuggerPublicApi |
There was a problem hiding this comment.
💬 suggestion: the types LogsGlobal and RumGlobal are deprecated in favor of DatadogLogs and DatadogRum. what about using DatadogDebugger for consistency?
| }) | ||
| ``` | ||
|
|
||
| If [Datadog RUM][3] is also initialized on the page, debugger snapshots automatically include RUM context (session, view, user action) without any additional configuration. |
There was a problem hiding this comment.
❓ question: How does this works? I must have missed something, I didn't see how this sdk collects sessions
There was a problem hiding this comment.
I think the text here should probably be slightly modified to read better, but the gist is that we tie the probe result to the current trace/span: https://github.com/DataDog/browser-sdk/pull/4449/files#diff-88cda10a3adeb04ab7401bf422650f85908ff2261c31b4411959b7c80be817d4R330-R341
There was a problem hiding this comment.
🤔 these fields (traceId and spanId) don't exist in RumInternalContext, is it really working?
There was a problem hiding this comment.
Ha interesting. I was actually looking for ways I could test this in an integration test without mocking it, but couldn't find a good way to do it, so I settled for a unit test after my AI had added it. Turns out my gut feeling was correct that this needed better testing
There was a problem hiding this comment.
We use session.id to correlate RUM event with logs, profiles and session replay.
Use the existing debugger service configuration when polling for probes so browser callers keep sending a valid delivery identity after the applicationId rollback. This also removes the stale applicationId init examples and test fixtures from the debugger package. Made-with: Cursor
| const rumContext = globalObj.DD_RUM?.getInternalContext?.() | ||
| const traceId = getStringContextValue(rumContext, 'trace_id') | ||
| const spanId = getStringContextValue(rumContext, 'span_id') | ||
|
|
||
| return traceId && spanId | ||
| ? { | ||
| trace_id: traceId, | ||
| span_id: spanId, | ||
| } | ||
| : undefined |
There was a problem hiding this comment.
issue: trace_id and span_id don't exist in getInternalContext
| const hasReplaceAll = typeof (String.prototype as any).replaceAll === 'function' | ||
| const replaceDots = hasReplaceAll | ||
| ? (str: string) => (str as string & { replaceAll: (s: string, r: string) => string }).replaceAll('.', '_') | ||
| : (str: string) => str.replace(/\./g, '_') |
There was a problem hiding this comment.
| const hasReplaceAll = typeof (String.prototype as any).replaceAll === 'function' | |
| const replaceDots = hasReplaceAll | |
| ? (str: string) => (str as string & { replaceAll: (s: string, r: string) => string }).replaceAll('.', '_') | |
| : (str: string) => str.replace(/\./g, '_') | |
| const replaceDots = (str: string) => str.replace(/\./g, '_') |
| let fn = functionCache.get(cacheKey) | ||
| if (!fn) { | ||
| // eslint-disable-next-line no-new-func, @typescript-eslint/no-implied-eval | ||
| fn = new Function(...contextKeys, fnBody) as (...args: any[]) => boolean |
There was a problem hiding this comment.
issue: eval is evil, we shouldn't rely on eval if possible. Best practice is often to use CSP that block such functionality.
Is there something specific in the language that requires "inline" JavaScript code? Else we can probably parse its AST and run it using JS without actually generating JS code.
|
|
||
| declare const __BUILD_ENV__SDK_VERSION__: string | ||
|
|
||
| const DELIVERY_API_PATH = '/api/ui/debugger/probe-delivery' |
There was a problem hiding this comment.
Question: what is this route? Is this supposed to live on the customer website?
| "name": "@datadog/browser-debugger", | ||
| "version": "6.32.0", |
There was a problem hiding this comment.
suggestion:: mark this as "private": true so we don't publish it until it's ready for customer consumpiton
| ;(window as any).USE_INSTRUMENTED = true | ||
| }) | ||
|
|
||
| // Load debugger SDK (using local build for now) |
There was a problem hiding this comment.
question: why not using the actual SDK?
| createTest('send debugger snapshot when instrumented function is called') | ||
| .withDebugger() | ||
| .run(async ({ intakeRegistry, flushEvents, page, browserName, servers }) => { | ||
| test.skip(browserName !== 'chromium', 'Debugger tests require Chromium') |
There was a problem hiding this comment.
question why debugger tests require Chromium?
| } | ||
| } | ||
|
|
||
| async function readDebuggerIntakeRequest( |
There was a problem hiding this comment.
suggestion: this is a copy of the readRumOrLogsIntakeRequest function. You can just re-use it

Motivation
Introduce the
@datadog/browser-debuggerpackage to enable Live Debugger in browser applications. This gives frontend developers the ability to add log probes to running applications, evaluate conditions, and inspect runtime state — all without redeploying or modifying source code.Changes
New package:
packages/debuggerA new
@datadog/browser-debuggerpackage that provides the full probe execution pipeline:domain/api.ts— Core instrumentation hooks (onEntry,onReturn,onThrow) that execute probes when instrumented functions are called, including condition evaluation, snapshot capture, template message rendering, and rate limiting.domain/activeEntries.ts— Tracks per-probe execution stacks for correlating entry/return/throw events, extracted to break the dependency cycle betweenapi.tsandprobes.ts.domain/probes.ts— Probe lifecycle management (add, remove, clear) with per-probe and global snapshot rate limiting. Compiles probe conditions and template segments on registration.domain/capture.ts— Deep value capture for arguments, locals, return values, and thrown errors with configurable reference depth, collection size limits, and string length limits.domain/expression.ts— Expression compiler that parses JSON expression trees (comparisons, logical operators, member access, string operations, etc.) into executable functions.domain/condition.ts— Probe condition evaluator that compiles and caches condition expressions.domain/template.ts— Template segment compiler and evaluator for rendering dynamic probe messages with runtime context.domain/stacktrace.ts— Stack trace capture and parsing fromErrorobjects.domain/deliveryApi.ts— Polling-based probe delivery client that fetches probe updates/deletions from the Delivery API using a cursor for incremental sync.transport/startDebuggerBatch.ts— Transport layer that reuses@datadog/browser-core's batch/flush infrastructure to send debugger snapshots to the logs intake.entries/main.ts— Public API surface (datadogDebugger.init()). Exposes$dd_entry/$dd_return/$dd_throw/$dd_probeshooks onglobalThisfor instrumented code. Defines the globalDD_DEBUGGERobject.Changes to
@datadog/browser-core'dd_debugger'as a valid source in configuration and transport types, mapped to'browser'for the SDK source.computeTransportConfigurationand theBatchtype so the debugger package can create its own transport.E2E test framework and scenarios
test/e2e/scenario/debugger.scenario.ts— 7 E2E test scenarios covering: basic snapshot sending, argument/return value capture, exception capture on throw, template message evaluation with expression segments, condition evaluation (both met and not met), and RUM correlation..withDebugger()builder method tocreateTest(),DebuggerIntakeRequesttype andintakeRegistry.debuggerEventsfor asserting on debugger events, debugger page setups for CDN/bundle/npm modes, and default debugger configuration.test/apps/vanilla/app.ts— Added@datadog/browser-debuggerimport andDEBUGGER_INITsupport so debugger E2E tests work in the npm setup.Performance benchmarks
test/apps/instrumentation-overhead/— Webpack test app for measuring instrumentation overhead with instrumented vs. uninstrumented function variants.test/performance/scenarios/instrumentationOverhead.scenario.ts— Benchmark scenario that stress-tests 10M function calls to measure the overhead of debugger instrumentation hooks.test/performance/createBenchmarkTest.ts— Extended withinstrumented_no_probesandinstrumented_with_probesscenario configurations and a dedicatedinjectDebuggerfunction.Tooling
scripts/build/build-test-apps.tsto include the new test app and to use resolution paths when installing peer dependencies (needed for unpublished packages like@datadog/browser-debuggerthat only exist locally as.tgzfiles).scripts/dev-server/lib/server.tsto serve the debugger bundle.Test instructions
yarn test:unit --spec "packages/debugger/src/**/*.spec.ts"yarn test:e2e:init && yarn test:e2e -g "debugger"yarn build:apps && yarn test:performanceChecklist
Tested on staging— N/A, this is a new pre-production package not yet deployed to any environment