Skip to content

✨ [RUM-10145] collect errors on module evaluation #3622

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 12 commits into from
Jul 17, 2025

Conversation

BenoitZugmeyer
Copy link
Member

@BenoitZugmeyer BenoitZugmeyer commented Jun 11, 2025

Motivation

Currently, the RUM and Logs Browser SDK are starting to collect errors when the following conditions are met:

  • the SDK is loaded
  • init() was called
  • tracking consent was granted (either via the trackingConsent init parameter or when setTrackingConsent(‘granted’) is called)
  • a RUM View was created (RUM only, in case trackViewsManually is used)
  • the Remote Configuration is fetched

To improve observability coverage, we want to collect errors earlier, and the easiest way to do so is to start collecting errors on module evaluation, buffer them, and send them when RUM is ready.

This is a first step. We chose to start with runtime errors first as it was deemed to be the most valuable without much drawback, but our approach needs to be easily extendable to collect other kind of values.

Also, in the future this mechanism could be separated from the main SDK to allow customer inline this code in their HTML to cover even more ground, mitigating the performance impact of inlining the whole SDK entirely.

Changes

Please review commit by commit.

We start by introducing a BufferedObserver, which is intended to be used for other purposes and eventually replace BoundedBuffer.

After a bit of refactoring in tests, we implement early runtime error collection in RUM and Logs by tracking runtime errors and putting them in a BufferedObserver instance.

Test instructions

In the sandbox (which uses a sync CDN setup), throw an exception before calling init, ex:

throw new Error('test')
setTimeout(() => {
  DD_RUM.init(...)
  DD_LOGS.init(...)
})

The error should be collected.

Checklist

  • Tested locally
  • Tested on staging
  • Added unit tests for this change.
  • Added e2e/integration tests for this change.

@BenoitZugmeyer BenoitZugmeyer force-pushed the benoit/early-data-collection-2 branch from 8a8b156 to fbe5f7b Compare June 11, 2025 15:47
@codecov-commenter
Copy link

codecov-commenter commented Jun 11, 2025

Codecov Report

Attention: Patch coverage is 96.20253% with 3 lines in your changes missing coverage. Please review.

Project coverage is 92.21%. Comparing base (2650637) to head (cafe16b).

Files with missing lines Patch % Lines
...kages/rum-core/src/domain/error/errorCollection.ts 50.00% 2 Missing ⚠️
packages/core/src/tools/queueMicrotask.ts 80.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main    #3622      +/-   ##
==========================================
- Coverage   92.21%   92.21%   -0.01%     
==========================================
  Files         329      331       +2     
  Lines        8209     8257      +48     
  Branches     1856     1864       +8     
==========================================
+ Hits         7570     7614      +44     
- Misses        639      643       +4     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@BenoitZugmeyer BenoitZugmeyer force-pushed the benoit/early-data-collection-2 branch 2 times, most recently from b25932e to 80e46f3 Compare June 11, 2025 15:59
}
const { stop: stopInstrumentingOnError } = instrumentOnError(handleRuntimeError)
const { stop: stopInstrumentingOnUnhandledRejection } = instrumentUnhandledRejection(handleRuntimeError)
export function trackRuntimeError() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

💬 suggestion: ‏Why not using BufferedObservable directly in here.

Copy link
Member Author

Choose a reason for hiding this comment

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

I tried, but:

  • It makes a circular dependency between trackRuntimeError and bufferedData
  • The "Buffered Data" concept leaks everywhere in trackRuntimeError. It makes it more complex as it's not scoped to rawError anymore.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Hum, fine by me! Let’s move forward and maybe refine the design later as this pattern is used more widely :)

@BenoitZugmeyer BenoitZugmeyer force-pushed the benoit/early-data-collection-2 branch from 80e46f3 to 8db9e24 Compare July 2, 2025 17:01
Copy link

cit-pr-commenter bot commented Jul 2, 2025

Bundles Sizes Evolution

📦 Bundle Name Base Size Local Size 𝚫 𝚫% Status
Rum 149.01 KiB 149.92 KiB 936 B +0.61%
Rum Recorder 17.91 KiB 17.91 KiB 0 B 0.00%
Rum Profiler 4.98 KiB 4.98 KiB 0 B 0.00%
Logs 52.87 KiB 53.62 KiB 769 B +1.42%
Flagging 0 B 931 B 931 B N/A%
Rum Slim 107.71 KiB 108.54 KiB 854 B +0.77%
Worker 23.60 KiB 23.60 KiB 0 B 0.00%
🚀 CPU Performance
Action Name Base Average Cpu Time (ms) Local Average Cpu Time (ms) 𝚫
addglobalcontext 0.020 0.012 -0.007
addaction 0.049 0.027 -0.022
addtiming 0.009 0.006 -0.003
adderror 0.045 0.026 -0.019
startstopsessionreplayrecording 0.008 0.004 -0.003
startview 0.016 0.009 -0.007
logmessage 0.060 0.026 -0.034
🧠 Memory Performance
Action Name Base Consumption Memory (bytes) Local Consumption Memory (bytes) 𝚫 (bytes)
addglobalcontext 26.34 KiB 27.79 KiB 1.45 KiB
addaction 52.99 KiB 55.23 KiB 2.24 KiB
addtiming 25.50 KiB 26.99 KiB 1.49 KiB
adderror 60.10 KiB 63.11 KiB 3.00 KiB
startstopsessionreplayrecording 24.86 KiB 25.65 KiB 807 B
startview 429.65 KiB 429.11 KiB -555 B
logmessage 53.27 KiB 61.24 KiB 7.97 KiB

🔗 RealWorld

@BenoitZugmeyer BenoitZugmeyer force-pushed the benoit/early-data-collection-2 branch from 8db9e24 to bd3bfb1 Compare July 3, 2025 10:47
@BenoitZugmeyer BenoitZugmeyer force-pushed the benoit/early-data-collection-2 branch from bd3bfb1 to a4f4d32 Compare July 3, 2025 12:18
@BenoitZugmeyer
Copy link
Member Author

/to-staging

@dd-devflow
Copy link
Contributor

dd-devflow bot commented Jul 3, 2025

View all feedbacks in Devflow UI.

2025-07-03 12:56:28 UTC ℹ️ Start processing command /to-staging


2025-07-03 12:56:39 UTC ℹ️ Branch Integration: starting soon, merge expected in approximately 12m46s (p90)

Commit a4f4d328cc will soon be integrated into staging-27.


2025-07-03 13:08:36 UTC ℹ️ Branch Integration: This commit was successfully integrated

Commit a4f4d328cc has been merged into staging-27 in merge commit 2da91af4e6.

Check out the triggered pipeline on Gitlab 🦊

If you need to revert this integration, you can use the following command: /code revert-integration -b staging-27

@BenoitZugmeyer BenoitZugmeyer marked this pull request as ready for review July 3, 2025 12:56
@BenoitZugmeyer BenoitZugmeyer requested a review from a team as a code owner July 3, 2025 12:56
dd-mergequeue bot added a commit that referenced this pull request Jul 3, 2025
@dd-devflow dd-devflow bot added the staging-27 label Jul 3, 2025
import type { RawError } from './error/error.types'
import { trackRuntimeError } from './error/trackRuntimeError'

const BUFFER_LIMIT = 500
Copy link
Contributor

Choose a reason for hiding this comment

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

💭 suggestion : Could we put a single const for the buffer limit or is it expected since the other is deprecated?
(see boundedBuffer.ts)

Copy link
Member Author

Choose a reason for hiding this comment

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

I'd like to keep BufferedObservable as generic as possible to encourage using it in more places. But I could add a default value in the BufferedObservable constructor, to keep it configurable while not having to define a limit every single time when it doesn't matter much. WDYT?

* work even if this method isn't called, but still useful to clarify our intent and lowering our
* memory impact.
*/
unbuffer() {
Copy link
Collaborator

@bcaudan bcaudan Jul 11, 2025

Choose a reason for hiding this comment

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

❓ question: ‏could the unbuffer be done without an explicit call after the first subscribe?

Copy link
Member Author

Choose a reason for hiding this comment

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

It could for now, as it is subscribed only once. But in the future I expect this observable to be used in multiple places where the buffer is leveraged (ex: we could imagine using the observable to generate resources, actions... not just errors).

Copy link
Collaborator

@bcaudan bcaudan left a comment

Choose a reason for hiding this comment

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

Great work!

@BenoitZugmeyer

This comment was marked as outdated.

@dd-devflow-routing-codex

This comment was marked as outdated.

@BenoitZugmeyer
Copy link
Member Author

/to-staging

@dd-devflow-routing-codex
Copy link

dd-devflow-routing-codex bot commented Jul 17, 2025

View all feedbacks in Devflow UI.

2025-07-17 09:12:40 UTC ℹ️ Start processing command /to-staging


2025-07-17 09:12:48 UTC ℹ️ Branch Integration: starting soon, merge expected in approximately 7m42s (p90)

Commit cafe16b8e6 will soon be integrated into staging-29.


2025-07-17 09:12:56 UTC ⚠️ Branch Integration: This commit was already integrated

Commit cafe16b8e6 had already been merged into staging-29

If you need to revert this integration, you can use the following command: /code revert-integration -b staging-29

@BenoitZugmeyer
Copy link
Member Author

/merge

@dd-devflow-routing-codex
Copy link

dd-devflow-routing-codex bot commented Jul 17, 2025

View all feedbacks in Devflow UI.

2025-07-17 13:02:43 UTC ℹ️ Start processing command /merge


2025-07-17 13:02:58 UTC ℹ️ MergeQueue: pull request added to the queue

The expected merge time in main is approximately 25m (p90).


2025-07-17 13:29:58 UTC ℹ️ MergeQueue: This merge request was merged

@dd-mergequeue dd-mergequeue bot merged commit 3d4cda8 into main Jul 17, 2025
22 checks passed
@dd-mergequeue dd-mergequeue bot deleted the benoit/early-data-collection-2 branch July 17, 2025 13:29
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.

6 participants