diff --git a/packages/auto-instrumentations-node/src/utils.ts b/packages/auto-instrumentations-node/src/utils.ts index ce5e1d1941..cda71e00f9 100644 --- a/packages/auto-instrumentations-node/src/utils.ts +++ b/packages/auto-instrumentations-node/src/utils.ts @@ -152,6 +152,50 @@ export type InstrumentationConfigMap = { >; }; +function shouldDisableInstrumentation( + name: string, + userConfig: any, + enabledInstrumentationsFromEnv: string[], + disabledInstrumentationsFromEnv: string[] +): boolean { + // Priority 1: Programmatic config + if (userConfig.enabled === false) { + diag.debug( + `Disabling instrumentation for ${name} - disabled by user config` + ); + return true; + } + + if (userConfig.enabled === true) { + diag.debug( + `Enabling instrumentation for ${name} - explicitly enabled by user config` + ); + return false; + } + + // Priority 2: Environment variables + if (disabledInstrumentationsFromEnv.includes(name)) { + diag.debug(`Disabling instrumentation for ${name} - disabled by env var`); + return true; + } + + const isEnabledEnvSet = !!process.env.OTEL_NODE_ENABLED_INSTRUMENTATIONS; + if (isEnabledEnvSet && !enabledInstrumentationsFromEnv.includes(name)) { + diag.debug( + `Disabling instrumentation for ${name} - not in enabled env var list` + ); + return true; + } + + // Priority 3: Default exclusions + if (!isEnabledEnvSet && defaultExcludedInstrumentations.includes(name)) { + diag.debug(`Disabling instrumentation for ${name} - excluded by default`); + return true; + } + + return false; +} + export function getNodeAutoInstrumentations( inputConfigs: InstrumentationConfigMap = {} ): Instrumentation[] { @@ -168,12 +212,14 @@ export function getNodeAutoInstrumentations( // Defaults are defined by the instrumentation itself const userConfig: any = inputConfigs[name] ?? {}; - if ( - userConfig.enabled === false || - !enabledInstrumentationsFromEnv.includes(name) || - disabledInstrumentationsFromEnv.includes(name) - ) { - diag.debug(`Disabling instrumentation for ${name}`); + const shouldDisable = shouldDisableInstrumentation( + name, + userConfig, + enabledInstrumentationsFromEnv, + disabledInstrumentationsFromEnv + ); + + if (shouldDisable) { continue; } diff --git a/packages/auto-instrumentations-node/test/utils.test.ts b/packages/auto-instrumentations-node/test/utils.test.ts index 1241ca6cce..c83fc520c0 100644 --- a/packages/auto-instrumentations-node/test/utils.test.ts +++ b/packages/auto-instrumentations-node/test/utils.test.ts @@ -172,6 +172,92 @@ describe('utils', () => { spy.restore(); }); + + it('should enable fs instrumentation when explicitly enabled and OTEL_NODE_ENABLED_INSTRUMENTATIONS is not set', () => { + const instrumentations = getNodeAutoInstrumentations({ + '@opentelemetry/instrumentation-fs': { + enabled: true, + }, + }); + const fsInstrumentation = instrumentations.find( + instr => + instr.instrumentationName === '@opentelemetry/instrumentation-fs' + ); + assert.notStrictEqual( + fsInstrumentation, + undefined, + 'fs instrumentation should be included when explicitly enabled in config and env var is not set' + ); + }); + + it('should respect programmatic config over OTEL_NODE_ENABLED_INSTRUMENTATIONS - user config overrides env var', () => { + process.env.OTEL_NODE_ENABLED_INSTRUMENTATIONS = 'fs,http'; + try { + const instrumentations = getNodeAutoInstrumentations({ + '@opentelemetry/instrumentation-fs': { + enabled: false, // User explicitly disables - should override env var + }, + '@opentelemetry/instrumentation-express': { + enabled: true, // User explicitly enables - should override env var + }, + }); + + const fsInstrumentation = instrumentations.find( + instr => + instr.instrumentationName === '@opentelemetry/instrumentation-fs' + ); + const httpInstrumentation = instrumentations.find( + instr => + instr.instrumentationName === '@opentelemetry/instrumentation-http' + ); + const expressInstrumentation = instrumentations.find( + instr => + instr.instrumentationName === + '@opentelemetry/instrumentation-express' + ); + + assert.strictEqual( + fsInstrumentation, + undefined, + 'fs should be disabled by user config despite env var' + ); + assert.notStrictEqual( + httpInstrumentation, + undefined, + 'http should be enabled by env var (no user override)' + ); + assert.notStrictEqual( + expressInstrumentation, + undefined, + 'express should be enabled by user config despite not being in env var list' + ); + } finally { + delete process.env.OTEL_NODE_ENABLED_INSTRUMENTATIONS; + } + }); + + it('should respect programmatic config over OTEL_NODE_DISABLED_INSTRUMENTATIONS - user config overrides env var', () => { + process.env.OTEL_NODE_DISABLED_INSTRUMENTATIONS = 'http'; + try { + const instrumentations = getNodeAutoInstrumentations({ + '@opentelemetry/instrumentation-http': { + enabled: true, // User explicitly enables - should override env var + }, + }); + const httpInstrumentation = instrumentations.find( + instr => + instr.instrumentationName === '@opentelemetry/instrumentation-http' + ); + + assert.notStrictEqual( + httpInstrumentation, + undefined, + 'http instrumentation should be enabled by user config despite OTEL_NODE_DISABLED_INSTRUMENTATIONS' + ); + } finally { + delete process.env.OTEL_NODE_DISABLED_INSTRUMENTATIONS; + } + }); }); describe('getResourceDetectorsFromEnv', () => {