Skip to content

[dop] [error-tracking]: derive debug_id from file content - #470

Merged
gh-worker-dd-mergequeue-cf854d[bot] merged 5 commits into
masterfrom
hugo.silva/fix-debug-id-key-path
Aug 3, 2026
Merged

[dop] [error-tracking]: derive debug_id from file content#470
gh-worker-dd-mergequeue-cf854d[bot] merged 5 commits into
masterfrom
hugo.silva/fix-debug-id-key-path

Conversation

@jhssilva

@jhssilva jhssilva commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Summary

The RUM plugin injects a debug_id into every built chunk's own JS content. This PR makes the error-tracking plugin extract that debug_id back out of each file when it uploads that file's sourcemap, instead of relying on the previous filename-based coordination between the two plugins.

Why

Previously, error-tracking got the debug_id from a side-channel Map that the RUM plugin populated at injection time, keyed by the chunk's filename. Bundlers (webpack/rspack) rename chunks after injection via realContentHash, so by the time error-tracking looked the value up by the chunk's final filename, the key no longer matched — the debug_id was silently dropped for almost every chunk in a real build.

How

Instead of coordinating through a filename-keyed Map, error-tracking now reads each minified file's own content directly and extracts the debug_id from the ddDebugId literal the RUM plugin already embeds inside it. This works no matter what renaming the bundler does afterward, since the debug_id travels with the file's content, not its name.

  • error-tracking/src/sourcemaps/debugId.ts (new): extractDebugId() — regex-extracts the debug_id from a file's content.
  • error-tracking/src/sourcemaps/sender.ts: reads the minified file's content and extracts the debug_id from it, in place of the old Map lookup.
  • plugins/rum/src/index.ts + getSourceCodeContextSnippet.ts: RUM no longer needs to track debug_ids separately — the value already lives in the code it injects.
  • core/src/helpers/fs.ts: new readFilePrefix() helper, since we only need the first bytes of each file, not the whole thing.
  • plugins/injection/{xpack,esbuild}.ts: keep injected chunk filenames as relative paths for consistency with the rest of the pipeline.

Notes

  • Real end-to-end build against a module-federation-remote entry (mf_apm_runtime_app, rspack), uploaded to staging: each nested chunk's uploaded debug_id matches the ddDebugId literal embedded in that chunk's own file.

@jhssilva
jhssilva requested review from a team as code owners July 28, 2026 17:28
@jhssilva
jhssilva requested review from aananiadis and removed request for a team July 28, 2026 17:28
@datadog-datadog-prod-us1-2

datadog-datadog-prod-us1-2 Bot commented Jul 28, 2026

Copy link
Copy Markdown

Tests

🎉 All green!

🧪 All tests passed
❄️ No new flaky tests detected

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: 3cbf4e6 | Docs | Datadog PR Page | Give us feedback!

// but the content, and the debug_id embedded in it, is unaffected. Only the file's
// prefix is read, since the RUM plugin's injected snippet always lands near the
// start of the file (see DEBUG_ID_SEARCH_PREFIX_BYTES).
const fileContent = await readFilePrefix(

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: With esbuild, this runs before the injection onEnd, so it uploads the pre-injection files without ddDebugId. Should't we defer the upload until injection completes.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For now, we'll be dogfooding in web-ui. We'll do other iterations as needed in future pr's.
We'll keep this in mind.

Comment thread packages/core/src/helpers/fs.ts Outdated
@jhssilva
jhssilva changed the base branch from buranmert/rum-debug-id-to-error-tracking to master July 31, 2026 15:03
@jhssilva jhssilva changed the title fix(error-tracking): derive debug_id from file content, not a filename Map [dop] [error-tracking]: derive debug_id from file content, not a filename Map Jul 31, 2026
@jhssilva jhssilva changed the title [dop] [error-tracking]: derive debug_id from file content, not a filename Map [dop] [error-tracking]: derive debug_id from file content Jul 31, 2026
@jhssilva jhssilva self-assigned this Jul 31, 2026
Comment thread packages/plugins/injection/src/xpack.ts Outdated
Comment thread packages/plugins/injection/src/esbuild.ts Outdated
Comment thread packages/core/src/helpers/fs.ts Outdated
…e Map

The RUM plugin injects a debug_id into every built chunk's own JS
content. error-tracking's sourcemap uploader now extracts that
debug_id back out of each file when it uploads that file's sourcemap,
instead of relying on filename-based coordination between the two
plugins.

Previously, error-tracking got the debug_id from a side-channel Map
that the RUM plugin populated at injection time, keyed by the chunk's
filename. Bundlers (webpack/rspack) rename chunks after injection via
realContentHash, so by the time error-tracking looked the value up by
the chunk's final filename, the key no longer matched — the debug_id
was silently dropped for almost every chunk in a real build.

Instead of coordinating through a filename-keyed Map, error-tracking
now reads each minified file's own content directly and extracts the
debug_id from the ddDebugId literal the RUM plugin already embeds
inside it. This works no matter what renaming the bundler does
afterward, since the debug_id travels with the file's content, not
its name.
@jhssilva
jhssilva force-pushed the hugo.silva/fix-debug-id-key-path branch from 9a8a985 to 9e20c37 Compare July 31, 2026 15:30
…op unrelated fileName plumbing

extractDebugId now takes a file path and owns the read+regex composition
directly, instead of sender.ts composing readFilePrefix + extractDebugId
itself. Removes the now-unneeded readFilePrefix import from sender.ts.

Also reverts the esbuild.ts/xpack.ts fileName changes back to bare
basenames — that relative-path plumbing was left over from the old
filename-keyed debug_id lookup this PR removes, and nothing in this
diff depends on it anymore.
jhssilva added 3 commits July 31, 2026 18:44
Per review, readFilePrefix had a single call site (debugId.ts) so it
didn't warrant living in the shared fs helpers. The byte-limited read
is now implemented directly inside debugId.ts.

Since debugId.ts's file read is no longer mockable through the shared
addFixtureFiles fixture system, its tests (and the one sender.ts test
that exercises real debug_id extraction) now write real temp files
instead, matching the pattern already used by fs.test.ts's own
(now-removed) readFilePrefix tests.
commit 52b5952 ("fold debug_id file read into debugId.ts, drop
unrelated fileName plumbing") collapsed `fileName` back down to a bare
basename in both xpack.ts and esbuild.ts, treating the relative-path
computation as unrelated scope creep and removing it along with the
comment explaining why it existed.

It wasn't unrelated: debug_id lookups match chunks by the same
relativePath computed for sourcemap uploads in
error-tracking/src/sourcemaps/files.ts. A bare basename works for
simple single-entry builds, but nested/federated chunks (e.g.
module-federation remotes) can share a basename across different
subdirectories, so collapsing to basename reintroduces collisions
between unrelated chunks during content/debug-id injection.

Restore the relative path (normalized to forward slashes) in both
bundler backends.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants