From 817002011b50187f9217dacb996611114570d191 Mon Sep 17 00:00:00 2001 From: Arya Date: Thu, 26 Mar 2026 15:09:44 +0530 Subject: [PATCH 1/6] refactor: normalize numeric config resolution with env precedence --- packages/core/src/config/index.js | 85 ++++++++++++++++--------------- 1 file changed, 43 insertions(+), 42 deletions(-) diff --git a/packages/core/src/config/index.js b/packages/core/src/config/index.js index dde9e8e041..05e09c2520 100644 --- a/packages/core/src/config/index.js +++ b/packages/core/src/config/index.js @@ -216,12 +216,12 @@ function normalizeMetricsConfig(config) { config.metrics = {}; } - config.metrics.transmissionDelay = normalizeSingleValue( - config.metrics.transmissionDelay, - defaults.metrics.transmissionDelay, - 'config.metrics.transmissionDelay', - 'INSTANA_METRICS_TRANSMISSION_DELAY' - ); + config.metrics.transmissionDelay = resolveNumericConfig({ + envVar: 'INSTANA_METRICS_TRANSMISSION_DELAY', + configValue: config.metrics.transmissionDelay, + defaultValue: defaults.metrics.transmissionDelay, + configPath: 'config.metrics.transmissionDelay' + }); config.metrics.timeBetweenHealthcheckCalls = config.metrics.timeBetweenHealthcheckCalls || defaults.metrics.timeBetweenHealthcheckCalls; @@ -372,12 +372,12 @@ function normalizeActivateImmediately(config) { function normalizeTracingTransmission(config) { config.tracing.maxBufferedSpans = config.tracing.maxBufferedSpans || defaults.tracing.maxBufferedSpans; - config.tracing.transmissionDelay = normalizeSingleValue( - config.tracing.transmissionDelay, - defaults.tracing.transmissionDelay, - 'config.tracing.transmissionDelay', - 'INSTANA_TRACING_TRANSMISSION_DELAY' - ); + config.tracing.transmissionDelay = resolveNumericConfig({ + envVar: 'INSTANA_TRACING_TRANSMISSION_DELAY', + configValue: config.tracing.transmissionDelay, + defaultValue: defaults.tracing.transmissionDelay, + configPath: 'config.tracing.transmissionDelay' + }); // DEPRECATED! This was never documented, but we shared it with a customer. if (process.env['INSTANA_DEV_MIN_DELAY_BEFORE_SENDING_SPANS']) { @@ -397,19 +397,19 @@ function normalizeTracingTransmission(config) { } } - config.tracing.forceTransmissionStartingAt = normalizeSingleValue( - config.tracing.forceTransmissionStartingAt, - defaults.tracing.forceTransmissionStartingAt, - 'config.tracing.forceTransmissionStartingAt', - 'INSTANA_FORCE_TRANSMISSION_STARTING_AT' - ); + config.tracing.forceTransmissionStartingAt = resolveNumericConfig({ + envVar: 'INSTANA_FORCE_TRANSMISSION_STARTING_AT', + configValue: config.tracing.forceTransmissionStartingAt, + defaultValue: defaults.tracing.forceTransmissionStartingAt, + configPath: 'config.tracing.forceTransmissionStartingAt' + }); - config.tracing.initialTransmissionDelay = normalizeSingleValue( - config.tracing.initialTransmissionDelay, - defaults.tracing.initialTransmissionDelay, - 'config.tracing.initialTransmissionDelay', - 'INSTANA_TRACING_INITIAL_TRANSMISSION_DELAY' - ); + config.tracing.initialTransmissionDelay = resolveNumericConfig({ + envVar: 'INSTANA_TRACING_INITIAL_TRANSMISSION_DELAY', + configValue: config.tracing.initialTransmissionDelay, + defaultValue: defaults.tracing.initialTransmissionDelay, + configPath: 'config.tracing.initialTransmissionDelay' + }); } /** @@ -714,31 +714,32 @@ function parseSecretsEnvVar(envVarValue) { } /** - * @param {*} configValue - * @param {*} defaultValue - * @param {string} configPath - * @param {string} envVarKey - * @returns {*} + * @param {Object} params + * @param {string} params.envVar + * @param {number|string|undefined|null} params.configValue + * @param {number} params.defaultValue + * @param {string} params.configPath + * @returns {number} */ -function normalizeSingleValue(configValue, defaultValue, configPath, envVarKey) { - const envVarVal = process.env[envVarKey]; - let originalValue = configValue; - if (configValue == null && envVarVal == null) { - return defaultValue; - } else if (configValue == null && envVarVal != null) { - originalValue = envVarVal; - configValue = parseInt(originalValue, 10); - } +function resolveNumericConfig({ envVar, configValue, defaultValue, configPath }) { + const envRaw = process.env[envVar]; + + const rawValue = envRaw != null ? envRaw : configValue != null ? configValue : defaultValue; + + const parsedValue = typeof rawValue === 'number' ? rawValue : Number(rawValue); + + if (typeof parsedValue !== 'number' || isNaN(parsedValue)) { + const source = envRaw != null ? `env:${envVar}` : configValue != null ? 'config' : 'default'; - if (typeof configValue !== 'number' || isNaN(configValue)) { logger.warn( - `The value of ${configPath} (or ${envVarKey}) ("${originalValue}") is ' + - 'not numerical or cannot be parsed to a numerical value. Assuming the default value ${defaultValue}.` + `Invalid numeric value for ${configPath} from ${source}: "${rawValue}". Using default: ${defaultValue}.` ); return defaultValue; } - return configValue; + + return parsedValue; } + /** * @param {InstanaConfig} config */ From 8738c37a744086fb2e4400293b59456f398624d4 Mon Sep 17 00:00:00 2001 From: Arya Date: Thu, 26 Mar 2026 15:20:25 +0530 Subject: [PATCH 2/6] chore: improved --- packages/core/src/config/index.js | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/packages/core/src/config/index.js b/packages/core/src/config/index.js index 05e09c2520..217e95fa79 100644 --- a/packages/core/src/config/index.js +++ b/packages/core/src/config/index.js @@ -724,20 +724,29 @@ function parseSecretsEnvVar(envVarValue) { function resolveNumericConfig({ envVar, configValue, defaultValue, configPath }) { const envRaw = process.env[envVar]; - const rawValue = envRaw != null ? envRaw : configValue != null ? configValue : defaultValue; + const parse = val => (typeof val === 'number' ? val : Number(val)); - const parsedValue = typeof rawValue === 'number' ? rawValue : Number(rawValue); + if (envRaw != null) { + const parsedEnv = parse(envRaw); + if (!isNaN(parsedEnv)) { + return parsedEnv; + } + + logger.warn(`Invalid numeric value from env:${envVar}: "${envRaw}". Ignoring and checking config value.`); + } - if (typeof parsedValue !== 'number' || isNaN(parsedValue)) { - const source = envRaw != null ? `env:${envVar}` : configValue != null ? 'config' : 'default'; + if (configValue != null) { + const parsedConfig = parse(configValue); + if (!isNaN(parsedConfig)) { + return parsedConfig; + } logger.warn( - `Invalid numeric value for ${configPath} from ${source}: "${rawValue}". Using default: ${defaultValue}.` + `Invalid numeric value for ${configPath} from config: "${configValue}". Falling back to default: ${defaultValue}.` ); - return defaultValue; } - return parsedValue; + return defaultValue; } /** From 04fb1d5e66281ab410a7fad3b9f3232247702591 Mon Sep 17 00:00:00 2001 From: Arya Date: Thu, 26 Mar 2026 15:26:01 +0530 Subject: [PATCH 3/6] chore: improved --- packages/core/src/config/index.js | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/packages/core/src/config/index.js b/packages/core/src/config/index.js index 217e95fa79..62abca6ad2 100644 --- a/packages/core/src/config/index.js +++ b/packages/core/src/config/index.js @@ -724,21 +724,25 @@ function parseSecretsEnvVar(envVarValue) { function resolveNumericConfig({ envVar, configValue, defaultValue, configPath }) { const envRaw = process.env[envVar]; - const parse = val => (typeof val === 'number' ? val : Number(val)); + /** @param {number|string|null|undefined} val */ + const toValidNumber = val => { + const num = typeof val === 'number' ? val : Number(val); + return isNaN(num) ? undefined : num; + }; if (envRaw != null) { - const parsedEnv = parse(envRaw); - if (!isNaN(parsedEnv)) { - return parsedEnv; + const envParsed = toValidNumber(envRaw); + if (envParsed !== undefined) { + return envParsed; } logger.warn(`Invalid numeric value from env:${envVar}: "${envRaw}". Ignoring and checking config value.`); } if (configValue != null) { - const parsedConfig = parse(configValue); - if (!isNaN(parsedConfig)) { - return parsedConfig; + const configParsed = toValidNumber(configValue); + if (configParsed !== undefined) { + return configParsed; } logger.warn( From 0ca545055d0c1e7283557a24137cd018b4310811 Mon Sep 17 00:00:00 2001 From: Arya Date: Thu, 26 Mar 2026 15:34:26 +0530 Subject: [PATCH 4/6] chore: added util --- packages/core/src/config/index.js | 42 +---- packages/core/src/config/util.js | 57 ++++++ packages/core/test/config/util_test.js | 241 +++++++++++++++++++++++++ 3 files changed, 300 insertions(+), 40 deletions(-) create mode 100644 packages/core/src/config/util.js create mode 100644 packages/core/test/config/util_test.js diff --git a/packages/core/src/config/index.js b/packages/core/src/config/index.js index 62abca6ad2..f33ee8d1b8 100644 --- a/packages/core/src/config/index.js +++ b/packages/core/src/config/index.js @@ -13,6 +13,7 @@ const configValidators = require('./configValidators'); const deepMerge = require('../util/deepMerge'); const { DEFAULT_STACK_TRACE_LENGTH, DEFAULT_STACK_TRACE_MODE } = require('../util/constants'); const { validateStackTraceMode, validateStackTraceLength } = require('./configValidators/stackTraceValidation'); +const { resolveNumericConfig } = require('./util'); /** * @typedef {Object} InstanaTracingOption @@ -141,6 +142,7 @@ module.exports.configValidators = configValidators; module.exports.init = _logger => { logger = _logger; configNormalizers.init({ logger }); + require('./util').init(logger); }; /** @@ -713,46 +715,6 @@ function parseSecretsEnvVar(envVarValue) { }; } -/** - * @param {Object} params - * @param {string} params.envVar - * @param {number|string|undefined|null} params.configValue - * @param {number} params.defaultValue - * @param {string} params.configPath - * @returns {number} - */ -function resolveNumericConfig({ envVar, configValue, defaultValue, configPath }) { - const envRaw = process.env[envVar]; - - /** @param {number|string|null|undefined} val */ - const toValidNumber = val => { - const num = typeof val === 'number' ? val : Number(val); - return isNaN(num) ? undefined : num; - }; - - if (envRaw != null) { - const envParsed = toValidNumber(envRaw); - if (envParsed !== undefined) { - return envParsed; - } - - logger.warn(`Invalid numeric value from env:${envVar}: "${envRaw}". Ignoring and checking config value.`); - } - - if (configValue != null) { - const configParsed = toValidNumber(configValue); - if (configParsed !== undefined) { - return configParsed; - } - - logger.warn( - `Invalid numeric value for ${configPath} from config: "${configValue}". Falling back to default: ${defaultValue}.` - ); - } - - return defaultValue; -} - /** * @param {InstanaConfig} config */ diff --git a/packages/core/src/config/util.js b/packages/core/src/config/util.js new file mode 100644 index 0000000000..78ff693ff9 --- /dev/null +++ b/packages/core/src/config/util.js @@ -0,0 +1,57 @@ +/* + * (c) Copyright IBM Corp. 2026 + */ + +'use strict'; + +/** @type {import('../core').GenericLogger} */ +let logger; + +/** + * @param {import('../core').GenericLogger} [_logger] + */ +exports.init = _logger => { + logger = _logger; +}; + +/** + * @param {Object} params + * @param {string} params.envVar + * @param {number|string|undefined|null} params.configValue + * @param {number} params.defaultValue + * @param {string} params.configPath + * @returns {number} + */ +function resolveNumericConfig({ envVar, configValue, defaultValue, configPath }) { + const envRaw = process.env[envVar]; + + /** @param {number|string|null|undefined} val */ + const toValidNumber = val => { + const num = typeof val === 'number' ? val : Number(val); + return Number.isNaN(num) ? undefined : num; + }; + + if (envRaw != null) { + const envParsed = toValidNumber(envRaw); + if (envParsed !== undefined) { + return envParsed; + } + + logger.warn(`Invalid numeric value from env:${envVar}: "${envRaw}". Ignoring and checking config value.`); + } + + if (configValue != null) { + const configParsed = toValidNumber(configValue); + if (configParsed !== undefined) { + return configParsed; + } + + logger.warn( + `Invalid numeric value for ${configPath} from config: "${configValue}". Falling back to default: ${defaultValue}.` + ); + } + + return defaultValue; +} + +exports.resolveNumericConfig = resolveNumericConfig; diff --git a/packages/core/test/config/util_test.js b/packages/core/test/config/util_test.js new file mode 100644 index 0000000000..dd92ff2357 --- /dev/null +++ b/packages/core/test/config/util_test.js @@ -0,0 +1,241 @@ +/* + * (c) Copyright IBM Corp. 2026 + */ + +'use strict'; + +const expect = require('chai').expect; +const { createFakeLogger } = require('../test_util'); +const util = require('../../src/config/util'); + +describe('config.util', () => { + let logger; + + before(() => { + logger = createFakeLogger(); + util.init(logger); + }); + + beforeEach(resetEnv); + afterEach(resetEnv); + + function resetEnv() { + delete process.env.TEST_ENV_VAR; + } + + describe('resolveNumericConfig', () => { + it('should return the default value when no env var or config value is provided', () => { + const result = util.resolveNumericConfig({ + envVar: 'TEST_ENV_VAR', + configValue: undefined, + defaultValue: 1000, + configPath: 'config.test.value' + }); + + expect(result).to.equal(1000); + }); + + it('should prioritize env var over config value', () => { + process.env.TEST_ENV_VAR = '2000'; + + const result = util.resolveNumericConfig({ + envVar: 'TEST_ENV_VAR', + configValue: 3000, + defaultValue: 1000, + configPath: 'config.test.value' + }); + + expect(result).to.equal(2000); + }); + + it('should use config value when env var is not set', () => { + const result = util.resolveNumericConfig({ + envVar: 'TEST_ENV_VAR', + configValue: 3000, + defaultValue: 1000, + configPath: 'config.test.value' + }); + + expect(result).to.equal(3000); + }); + + it('should handle numeric config value', () => { + const result = util.resolveNumericConfig({ + envVar: 'TEST_ENV_VAR', + configValue: 5000, + defaultValue: 1000, + configPath: 'config.test.value' + }); + + expect(result).to.equal(5000); + }); + + it('should handle string config value that can be parsed as number', () => { + const result = util.resolveNumericConfig({ + envVar: 'TEST_ENV_VAR', + configValue: '5000', + defaultValue: 1000, + configPath: 'config.test.value' + }); + + expect(result).to.equal(5000); + }); + + it('should handle string env var that can be parsed as number', () => { + process.env.TEST_ENV_VAR = '7500'; + + const result = util.resolveNumericConfig({ + envVar: 'TEST_ENV_VAR', + configValue: undefined, + defaultValue: 1000, + configPath: 'config.test.value' + }); + + expect(result).to.equal(7500); + }); + + it('should fall back to default when env var is invalid', () => { + process.env.TEST_ENV_VAR = 'not-a-number'; + + const result = util.resolveNumericConfig({ + envVar: 'TEST_ENV_VAR', + configValue: undefined, + defaultValue: 1000, + configPath: 'config.test.value' + }); + + expect(result).to.equal(1000); + }); + + it('should fall back to default when config value is invalid', () => { + const result = util.resolveNumericConfig({ + envVar: 'TEST_ENV_VAR', + configValue: 'invalid', + defaultValue: 1000, + configPath: 'config.test.value' + }); + + expect(result).to.equal(1000); + }); + + it('should use config value when env var is invalid', () => { + process.env.TEST_ENV_VAR = 'not-a-number'; + + const result = util.resolveNumericConfig({ + envVar: 'TEST_ENV_VAR', + configValue: 3000, + defaultValue: 1000, + configPath: 'config.test.value' + }); + + expect(result).to.equal(3000); + }); + + it('should handle zero as a valid value from env var', () => { + process.env.TEST_ENV_VAR = '0'; + + const result = util.resolveNumericConfig({ + envVar: 'TEST_ENV_VAR', + configValue: undefined, + defaultValue: 1000, + configPath: 'config.test.value' + }); + + expect(result).to.equal(0); + }); + + it('should handle zero as a valid value from config', () => { + const result = util.resolveNumericConfig({ + envVar: 'TEST_ENV_VAR', + configValue: 0, + defaultValue: 1000, + configPath: 'config.test.value' + }); + + expect(result).to.equal(0); + }); + + it('should handle negative numbers from env var', () => { + process.env.TEST_ENV_VAR = '-500'; + + const result = util.resolveNumericConfig({ + envVar: 'TEST_ENV_VAR', + configValue: undefined, + defaultValue: 1000, + configPath: 'config.test.value' + }); + + expect(result).to.equal(-500); + }); + + it('should handle negative numbers from config', () => { + const result = util.resolveNumericConfig({ + envVar: 'TEST_ENV_VAR', + configValue: -500, + defaultValue: 1000, + configPath: 'config.test.value' + }); + + expect(result).to.equal(-500); + }); + + it('should handle floating point numbers from env var', () => { + process.env.TEST_ENV_VAR = '123.45'; + + const result = util.resolveNumericConfig({ + envVar: 'TEST_ENV_VAR', + configValue: undefined, + defaultValue: 1000, + configPath: 'config.test.value' + }); + + expect(result).to.equal(123.45); + }); + + it('should handle floating point numbers from config', () => { + const result = util.resolveNumericConfig({ + envVar: 'TEST_ENV_VAR', + configValue: 123.45, + defaultValue: 1000, + configPath: 'config.test.value' + }); + + expect(result).to.equal(123.45); + }); + + it('should handle null config value', () => { + const result = util.resolveNumericConfig({ + envVar: 'TEST_ENV_VAR', + configValue: null, + defaultValue: 1000, + configPath: 'config.test.value' + }); + + expect(result).to.equal(1000); + }); + + it('should handle empty string env var as 0', () => { + process.env.TEST_ENV_VAR = ''; + + const result = util.resolveNumericConfig({ + envVar: 'TEST_ENV_VAR', + configValue: undefined, + defaultValue: 1000, + configPath: 'config.test.value' + }); + + expect(result).to.equal(0); + }); + + it('should handle empty string config value as 0', () => { + const result = util.resolveNumericConfig({ + envVar: 'TEST_ENV_VAR', + configValue: '', + defaultValue: 1000, + configPath: 'config.test.value' + }); + + expect(result).to.equal(0); + }); + }); +}); From 6a82c074b089790e91520c8530e8dad8290ac339 Mon Sep 17 00:00:00 2001 From: Arya Date: Thu, 26 Mar 2026 15:40:35 +0530 Subject: [PATCH 5/6] chore: updated --- packages/core/src/config/index.js | 10 +++++----- packages/core/src/config/util.js | 6 ++---- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/packages/core/src/config/index.js b/packages/core/src/config/index.js index f33ee8d1b8..288472551b 100644 --- a/packages/core/src/config/index.js +++ b/packages/core/src/config/index.js @@ -13,7 +13,7 @@ const configValidators = require('./configValidators'); const deepMerge = require('../util/deepMerge'); const { DEFAULT_STACK_TRACE_LENGTH, DEFAULT_STACK_TRACE_MODE } = require('../util/constants'); const { validateStackTraceMode, validateStackTraceLength } = require('./configValidators/stackTraceValidation'); -const { resolveNumericConfig } = require('./util'); +const util = require('./util'); /** * @typedef {Object} InstanaTracingOption @@ -218,7 +218,7 @@ function normalizeMetricsConfig(config) { config.metrics = {}; } - config.metrics.transmissionDelay = resolveNumericConfig({ + config.metrics.transmissionDelay = util.resolveNumericConfig({ envVar: 'INSTANA_METRICS_TRANSMISSION_DELAY', configValue: config.metrics.transmissionDelay, defaultValue: defaults.metrics.transmissionDelay, @@ -374,7 +374,7 @@ function normalizeActivateImmediately(config) { function normalizeTracingTransmission(config) { config.tracing.maxBufferedSpans = config.tracing.maxBufferedSpans || defaults.tracing.maxBufferedSpans; - config.tracing.transmissionDelay = resolveNumericConfig({ + config.tracing.transmissionDelay = util.resolveNumericConfig({ envVar: 'INSTANA_TRACING_TRANSMISSION_DELAY', configValue: config.tracing.transmissionDelay, defaultValue: defaults.tracing.transmissionDelay, @@ -399,14 +399,14 @@ function normalizeTracingTransmission(config) { } } - config.tracing.forceTransmissionStartingAt = resolveNumericConfig({ + config.tracing.forceTransmissionStartingAt = util.resolveNumericConfig({ envVar: 'INSTANA_FORCE_TRANSMISSION_STARTING_AT', configValue: config.tracing.forceTransmissionStartingAt, defaultValue: defaults.tracing.forceTransmissionStartingAt, configPath: 'config.tracing.forceTransmissionStartingAt' }); - config.tracing.initialTransmissionDelay = resolveNumericConfig({ + config.tracing.initialTransmissionDelay = util.resolveNumericConfig({ envVar: 'INSTANA_TRACING_INITIAL_TRANSMISSION_DELAY', configValue: config.tracing.initialTransmissionDelay, defaultValue: defaults.tracing.initialTransmissionDelay, diff --git a/packages/core/src/config/util.js b/packages/core/src/config/util.js index 78ff693ff9..fac00095d8 100644 --- a/packages/core/src/config/util.js +++ b/packages/core/src/config/util.js @@ -22,7 +22,7 @@ exports.init = _logger => { * @param {string} params.configPath * @returns {number} */ -function resolveNumericConfig({ envVar, configValue, defaultValue, configPath }) { +exports.resolveNumericConfig = function resolveNumericConfig({ envVar, configValue, defaultValue, configPath }) { const envRaw = process.env[envVar]; /** @param {number|string|null|undefined} val */ @@ -52,6 +52,4 @@ function resolveNumericConfig({ envVar, configValue, defaultValue, configPath }) } return defaultValue; -} - -exports.resolveNumericConfig = resolveNumericConfig; +}; From ffc7ad052570bf044ff26cbde346fcda2ce8dbab Mon Sep 17 00:00:00 2001 From: Arya Date: Thu, 26 Mar 2026 15:50:21 +0530 Subject: [PATCH 6/6] chore: updated --- packages/core/src/config/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/src/config/index.js b/packages/core/src/config/index.js index 288472551b..ba65c195a2 100644 --- a/packages/core/src/config/index.js +++ b/packages/core/src/config/index.js @@ -142,7 +142,7 @@ module.exports.configValidators = configValidators; module.exports.init = _logger => { logger = _logger; configNormalizers.init({ logger }); - require('./util').init(logger); + util.init(logger); }; /**