Skip to content

Conversation

@RulaKhaled
Copy link
Member

@RulaKhaled RulaKhaled commented Oct 20, 2025

When using higher-level integrations that wrap underlying libraries, both the wrapper integration and the underlying library integration can instrument the same API calls, resulting in duplicate spans. This is particularly problematic for:

  • LangChain wrapping AI providers (OpenAI, Anthropic, Google GenAI)
  • Any future integrations that wrap other instrumented libraries

The disabled integrations mechanism can be used as follows:

// Any integration can disable others to prevent conflicts
import { disableIntegrations, isIntegrationDisabled } from '@sentry/node-core';

// In higher-level integration's setupOnce():
disableIntegrations('LowerLevelIntegration');

// In integration setupOnce() is not called for the disabled integrations 

and is used in LangChain to auto disable OpenAI, Anthropic AI, and Google GenAI integrations in setupOnce().

@github-actions
Copy link
Contributor

github-actions bot commented Oct 20, 2025

size-limit report 📦

Path Size % Change Change
@sentry/browser 24.66 kB +0.08% +18 B 🔺
@sentry/browser - with treeshaking flags 23.16 kB +0.14% +31 B 🔺
@sentry/browser (incl. Tracing) 41.28 kB +0.05% +20 B 🔺
@sentry/browser (incl. Tracing, Profiling) 45.57 kB +0.07% +29 B 🔺
@sentry/browser (incl. Tracing, Replay) 79.51 kB +0.01% +1 B 🔺
@sentry/browser (incl. Tracing, Replay) - with treeshaking flags 69.23 kB +0.05% +30 B 🔺
@sentry/browser (incl. Tracing, Replay with Canvas) 84.22 kB +0.02% +9 B 🔺
@sentry/browser (incl. Tracing, Replay, Feedback) 96.42 kB +0.05% +45 B 🔺
@sentry/browser (incl. Feedback) 41.33 kB +0.06% +21 B 🔺
@sentry/browser (incl. sendFeedback) 29.32 kB +0.05% +14 B 🔺
@sentry/browser (incl. FeedbackAsync) 34.26 kB +0.05% +16 B 🔺
@sentry/react 26.35 kB +0.1% +24 B 🔺
@sentry/react (incl. Tracing) 43.26 kB +0.04% +13 B 🔺
@sentry/vue 29.15 kB +0.09% +24 B 🔺
@sentry/vue (incl. Tracing) 43.09 kB +0.1% +39 B 🔺
@sentry/svelte 24.68 kB +0.13% +31 B 🔺
CDN Bundle 26.92 kB +0.09% +23 B 🔺
CDN Bundle (incl. Tracing) 41.8 kB +0.01% +4 B 🔺
CDN Bundle (incl. Tracing, Replay) 78.09 kB +0.04% +24 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) 83.57 kB +0.02% +9 B 🔺
CDN Bundle - uncompressed 78.93 kB +0.05% +39 B 🔺
CDN Bundle (incl. Tracing) - uncompressed 124.05 kB +0.04% +39 B 🔺
CDN Bundle (incl. Tracing, Replay) - uncompressed 239.24 kB +0.02% +39 B 🔺
CDN Bundle (incl. Tracing, Replay, Feedback) - uncompressed 252 kB +0.02% +39 B 🔺
@sentry/nextjs (client) 45.39 kB +0.07% +30 B 🔺
@sentry/sveltekit (client) 41.68 kB +0.03% +11 B 🔺
@sentry/node-core 50.88 kB +0.14% +67 B 🔺
@sentry/node 157.98 kB +0.06% +94 B 🔺
@sentry/node - without tracing 92.78 kB +0.09% +82 B 🔺
@sentry/aws-serverless 106.47 kB +0.05% +48 B 🔺

View base workflow run

@github-actions
Copy link
Contributor

github-actions bot commented Oct 20, 2025

node-overhead report 🧳

Note: This is a synthetic benchmark with a minimal express app and does not necessarily reflect the real-world performance impact in an application.

Scenario Requests/s % of Baseline Prev. Requests/s Change %
GET Baseline 8,677 - 8,786 -1%
GET With Sentry 1,331 15% 1,286 +3%
GET With Sentry (error only) 6,042 70% 6,012 +0%
POST Baseline 1,198 - 1,157 +4%
POST With Sentry 497 41% 470 +6%
POST With Sentry (error only) 1,041 87% 1,009 +3%
MYSQL Baseline 3,291 - 3,242 +2%
MYSQL With Sentry 447 14% 391 +14%
MYSQL With Sentry (error only) 2,664 81% 2,614 +2%

View base workflow run

@RulaKhaled RulaKhaled force-pushed the disable-integrations branch from fd71786 to 79b5d89 Compare October 22, 2025 14:50
@RulaKhaled RulaKhaled force-pushed the disable-integrations branch from 470034b to 31cce57 Compare October 23, 2025 09:14
@RulaKhaled RulaKhaled marked this pull request as ready for review October 23, 2025 09:16
@cursor
Copy link

cursor bot commented Oct 23, 2025

Bug: Race Condition in AI Provider Setup

A race condition exists where the LangChain integration's setupOnce() method disables other AI provider integrations to prevent duplicate spans. However, if an AI provider's setupOnce() runs before LangChain's due to unpredictable integration setup order, it will instrument itself, leading to duplicate spans that this disabling mechanism intends to prevent.

Fix in Cursor Fix in Web

@RulaKhaled RulaKhaled marked this pull request as draft October 23, 2025 12:44
@RulaKhaled RulaKhaled changed the title feat(node-core): Add integration disabling mechanism to prevent instrumentation conflicts wip feat(node-core): Add integration disabling mechanism to prevent instrumentation conflicts Oct 23, 2025
@RulaKhaled RulaKhaled changed the title wip feat(node-core): Add integration disabling mechanism to prevent instrumentation conflicts feat(node-core): Add integration disabling mechanism to prevent instrumentation conflicts Nov 4, 2025
@RulaKhaled RulaKhaled marked this pull request as ready for review November 4, 2025 15:52
Copy link
Member

@s1gr1d s1gr1d left a comment

Choose a reason for hiding this comment

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

Nice! I added some comments regarding the current naming of the functions as they kinda imply a different meaning to what they are actually doing and I think it would be good to give this another look when we expose those functions to our users.

And maybe it's also worth to add some tests

Comment on lines +60 to +65
DISABLED_INTEGRATIONS.forEach(integrationName => {
const index = installedIntegrations.indexOf(integrationName);
if (index !== -1) {
installedIntegrations.splice(index, 1);
}
});
Copy link
Member

Choose a reason for hiding this comment

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

What do you think of using .filter() here so it's easier to comprehend what the code is doing.

installedIntegrations = installedIntegrations.filter(
    integration => !DISABLED_INTEGRATIONS.has(integration)
  );

Comment on lines +30 to +49
/**
* Check if an integration has been disabled.
* @param integrationName The name of the integration to check
* @returns true if the integration is disabled
*/
export function isIntegrationDisabled(integrationName: string): boolean {
return DISABLED_INTEGRATIONS.has(integrationName);
}

/**
* Remove one or more integrations from the disabled list.
* @param integrationName The name(s) of the integration(s) to enable
*/
export function enableIntegration(integrationName: string | string[]): void {
if (Array.isArray(integrationName)) {
integrationName.forEach(name => DISABLED_INTEGRATIONS.delete(name));
} else {
DISABLED_INTEGRATIONS.delete(integrationName);
}
}
Copy link
Member

Choose a reason for hiding this comment

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

Do we need those functions in the future?
Right now, they are not used and after calling init the Set will be empty and not used anymore if I understand correctly.

Exposing those methods to users might give them the false impression they can enable/disable integrations even though the method is only checking the Set.

* Mark one or more integrations as disabled to prevent their instrumentation from being set up.
* @param integrationName The name(s) of the integration(s) to disable
*/
export function disableIntegrations(integrationName: string | string[]): void {
Copy link
Member

Choose a reason for hiding this comment

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

I think the UX of the current approach could be improved. For example, the function name implies this is going to disable the integrations, but they are just marked and added to a Set, so no modification is happening at this point. Maybe the naming could be improved a bit so the flow of the functionality is clearer 🤔

Something like this:

  1. Call markAsDisabledIntegrations in the integration setup
  2. Call removeIntegrationsToDisable in init

Maybe you or @andreiborza have some better ideas for the naming

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants