@instana/core
Advanced tools
| /* | ||
| * (c) Copyright IBM Corp. 2026 | ||
| */ | ||
| 'use strict'; | ||
| const { CONFIG_SOURCES } = require('../util/constants'); | ||
| const SOURCE_LABELS = { | ||
| [CONFIG_SOURCES.ENV]: 'env', | ||
| [CONFIG_SOURCES.INCODE]: 'incode', | ||
| [CONFIG_SOURCES.AGENT]: 'agent', | ||
| [CONFIG_SOURCES.DEFAULT]: 'default' | ||
| }; | ||
| /** @type {import('../core').GenericLogger} */ | ||
| let logger; | ||
| /** | ||
| * @param {import('../core').GenericLogger} [_logger] | ||
| */ | ||
| exports.init = _logger => { | ||
| logger = _logger; | ||
| }; | ||
| const CONFIG_PRIORITY = Object.entries(CONFIG_SOURCES) | ||
| .sort((a, b) => a[1] - b[1]) | ||
| .map(([key]) => { | ||
| return key.toLowerCase(); | ||
| }); | ||
| /** | ||
| * | ||
| * @param {Object} params | ||
| * @param {string} [params.envValue] | ||
| * @param {any} [params.inCodeValue] | ||
| * @param {any} [params.agentValue] | ||
| * @param {any} params.defaultValue | ||
| * @param {Function|Function[]} validators - validator(s) returning value | undefined | ||
| * @returns {{ value: any, source: number }} | ||
| */ | ||
| exports.resolve = function resolve({ envValue, inCodeValue, agentValue, defaultValue }, validators) { | ||
| let resolved; | ||
| const validatorList = Array.isArray(validators) ? validators : [validators]; | ||
| const inputs = { | ||
| env: envValue ? process.env[envValue] : undefined, | ||
| incode: inCodeValue, | ||
| agent: agentValue, | ||
| default: defaultValue | ||
| }; | ||
| CONFIG_PRIORITY.some(sourceKey => { | ||
| const rawValue = inputs[/** @type {keyof typeof inputs} */ (sourceKey)]; | ||
| if (rawValue === undefined && sourceKey !== 'default') { | ||
| return false; | ||
| } | ||
| const parsedValue = validatorList.reduce((val, fn) => { | ||
| if (val === undefined) return undefined; | ||
| return fn(val); | ||
| }, rawValue); | ||
| if (parsedValue !== undefined) { | ||
| resolved = { | ||
| value: parsedValue, | ||
| source: CONFIG_SOURCES[/** @type {keyof typeof CONFIG_SOURCES} */ (sourceKey.toUpperCase())] | ||
| }; | ||
| return true; | ||
| } | ||
| return false; | ||
| }); | ||
| return ( | ||
| resolved || { | ||
| value: defaultValue, | ||
| source: CONFIG_SOURCES.DEFAULT | ||
| } | ||
| ); | ||
| }; | ||
| /** | ||
| * @param {{ configPath: string, source: number, value: any, envVarName?: string }} params | ||
| */ | ||
| exports.log = function log({ configPath, source, value, envVarName }) { | ||
| if (source === CONFIG_SOURCES.DEFAULT) { | ||
| return; | ||
| } | ||
| if (source === CONFIG_SOURCES.ENV && envVarName) { | ||
| logger?.debug(`[config] ${configPath} <- env:${envVarName} = ${JSON.stringify(value)}`); | ||
| return; | ||
| } | ||
| logger?.debug(`[config] ${configPath} <- ${SOURCE_LABELS[source]}:${configPath} = ${JSON.stringify(value)}`); | ||
| }; |
| /* | ||
| * (c) Copyright IBM Corp. 2026 | ||
| */ | ||
| 'use strict'; | ||
| /** | ||
| * @param {any} value | ||
| * @returns {number|undefined} | ||
| */ | ||
| exports.numberValidator = function numberValidator(value) { | ||
| if (value == null) return undefined; | ||
| const num = typeof value === 'number' ? value : Number(value); | ||
| return Number.isNaN(num) ? undefined : num; | ||
| }; | ||
| /** | ||
| * @param {any} value | ||
| * @returns {boolean|undefined} | ||
| */ | ||
| exports.booleanValidator = function booleanValidator(value) { | ||
| if (value == null) return undefined; | ||
| if (typeof value === 'boolean') return value; | ||
| if (typeof value === 'string') { | ||
| const normalized = value.toLowerCase(); | ||
| if (normalized === 'true' || normalized === '1') return true; | ||
| if (normalized === 'false' || normalized === '0') return false; | ||
| } | ||
| return undefined; | ||
| }; | ||
| /** | ||
| * @param {any} value | ||
| * @returns {string|undefined} | ||
| */ | ||
| exports.stringValidator = function stringValidator(value) { | ||
| if (value == null) return undefined; | ||
| return typeof value === 'string' ? value : undefined; | ||
| }; | ||
| /** | ||
| * @param {any} value | ||
| * @returns {boolean|undefined} | ||
| */ | ||
| exports.validateTruthyBoolean = function validateTruthyBoolean(value) { | ||
| // Return true if value is truthy, undefined otherwise | ||
| return value ? true : undefined; | ||
| }; |
+2
-3
| { | ||
| "name": "@instana/core", | ||
| "version": "5.5.0", | ||
| "version": "6.0.0", | ||
| "description": "Core library for Instana's Node.js packages", | ||
@@ -82,4 +82,3 @@ "main": "src/index.js", | ||
| "js-yaml": "^4.1.1" | ||
| }, | ||
| "gitHead": "da9ae25a9d8f772b67c12e9cd0070f9a01e74fb2" | ||
| } | ||
| } |
@@ -8,2 +8,3 @@ /* | ||
| const { DISABLABLE_INSTRUMENTATION_GROUPS } = require('../../tracing/constants'); | ||
| const { CONFIG_SOURCES } = require('../../util/constants'); | ||
| /** @type {import('../../core').GenericLogger} */ | ||
@@ -23,4 +24,4 @@ let logger; | ||
| * Precedence order (highest to lowest): | ||
| * 1. `tracing.disable` | ||
| * 2. Environment variables (`INSTANA_TRACING_DISABLE*`) | ||
| * 1. Environment variables (`INSTANA_TRACING_DISABLE*`) | ||
| * 2. In-code tracing.disable | ||
| * | ||
@@ -32,22 +33,42 @@ * @param {import('../../config').InstanaConfig} config | ||
| try { | ||
| // Disable all tracing if explicitly set 'disable' to true | ||
| const envDisableConfig = getDisableFromEnv(); | ||
| if (envDisableConfig !== null) { | ||
| if (envDisableConfig === true) { | ||
| return { value: true, source: CONFIG_SOURCES.ENV }; | ||
| } | ||
| if (envDisableConfig === false) { | ||
| return { value: {}, source: CONFIG_SOURCES.ENV }; | ||
| } | ||
| if (envDisableConfig.instrumentations?.length || envDisableConfig.groups?.length) { | ||
| logger?.debug(`[config] env:INSTANA_TRACING_DISABLE* = ${JSON.stringify(envDisableConfig)}`); | ||
| if (envDisableConfig.instrumentations) { | ||
| envDisableConfig.instrumentations = normalizeArray(envDisableConfig.instrumentations); | ||
| } | ||
| if (envDisableConfig.groups) { | ||
| envDisableConfig.groups = normalizeArray(envDisableConfig.groups); | ||
| } | ||
| return { value: envDisableConfig, source: CONFIG_SOURCES.ENV }; | ||
| } | ||
| } | ||
| if (config.tracing.disable === true) { | ||
| logger?.info('Tracing has been disabled via "tracing.disable: true" configuration.'); | ||
| return true; | ||
| logger?.debug('[config] incode:tracing.disable = true'); | ||
| return { value: true, source: CONFIG_SOURCES.INCODE }; | ||
| } | ||
| const hasDisableConfig = isDisableConfigNonEmpty(config); | ||
| if (hasDisableConfig) { | ||
| logger?.info( | ||
| `Tracing selectively disabled as per "tracing.disable" configuration: ${JSON.stringify(config.tracing.disable)}` | ||
| ); | ||
| logger?.debug(`[config] incode:tracing.disable = ${JSON.stringify(config.tracing.disable)}`); | ||
| } | ||
| // Fallback to environment variables if `disable` is not explicitly configured | ||
| const disableConfig = isDisableConfigNonEmpty(config) ? config.tracing.disable : getDisableFromEnv(); | ||
| const disableConfig = isDisableConfigNonEmpty(config) ? config.tracing.disable : null; | ||
| if (!disableConfig) return {}; | ||
| if (!disableConfig) return { value: {}, source: CONFIG_SOURCES.DEFAULT }; | ||
| if (disableConfig === true) return true; | ||
| // Normalize instrumentations and groups | ||
@@ -63,10 +84,10 @@ if (disableConfig?.instrumentations) { | ||
| if (Array.isArray(disableConfig)) { | ||
| return categorizeDisableEntries(disableConfig); | ||
| return { value: categorizeDisableEntries(disableConfig), source: CONFIG_SOURCES.INCODE }; | ||
| } | ||
| return disableConfig || {}; | ||
| return { value: disableConfig || {}, source: CONFIG_SOURCES.INCODE }; | ||
| } catch (error) { | ||
| // Fallback to an empty disable config on error | ||
| logger?.debug(`Error while normalizing tracing.disable config: ${error?.message} ${error?.stack}`); | ||
| return {}; | ||
| return { value: {}, source: CONFIG_SOURCES.DEFAULT }; | ||
| } | ||
@@ -83,3 +104,3 @@ }; | ||
| const flattenedEntries = flattenDisableConfigs(config.tracing.disable); | ||
| return categorizeDisableEntries(flattenedEntries); | ||
| return { value: categorizeDisableEntries(flattenedEntries), source: CONFIG_SOURCES.AGENT }; | ||
| } | ||
@@ -90,3 +111,3 @@ } catch (error) { | ||
| return {}; | ||
| return { value: {}, source: CONFIG_SOURCES.DEFAULT }; | ||
| }; | ||
@@ -100,3 +121,3 @@ | ||
| * | ||
| * @returns {import('../../config/types').Disable} | ||
| * @returns {import('../../config/types').Disable | boolean | null} | ||
| */ | ||
@@ -111,7 +132,12 @@ function getDisableFromEnv() { | ||
| if (envVarValue === 'true') { | ||
| logger?.info('Tracing has been disabled via environment variable "INSTANA_TRACING_DISABLE=true".'); | ||
| logger?.debug('[config] env:INSTANA_TRACING_DISABLE = true'); | ||
| return true; | ||
| } | ||
| if (envVarValue !== 'false' && envVarValue !== '') { | ||
| if (envVarValue === 'false') { | ||
| logger?.debug('[config] env:INSTANA_TRACING_DISABLE = false'); | ||
| return false; | ||
| } | ||
| if (envVarValue !== '') { | ||
| const categorized = categorizeDisableEntries(parseEnvVar(envVarValue)); | ||
@@ -125,3 +151,3 @@ if (categorized?.instrumentations?.length) { | ||
| logger?.info(`Tracing has been disabled via "INSTANA_TRACING_DISABLE=${envVarValue}"`); | ||
| logger?.debug(`[config] env:INSTANA_TRACING_DISABLE = ${envVarValue}`); | ||
| } | ||
@@ -132,6 +158,4 @@ } | ||
| disable.instrumentations = parseEnvVar(process.env.INSTANA_TRACING_DISABLE_INSTRUMENTATIONS); | ||
| logger?.info( | ||
| `Tracing instrumentations disabled via "INSTANA_TRACING_DISABLE_INSTRUMENTATIONS": ${JSON.stringify( | ||
| disable.instrumentations | ||
| )}` | ||
| logger?.debug( | ||
| `[config] env:INSTANA_TRACING_DISABLE_INSTRUMENTATIONS = ${process.env.INSTANA_TRACING_DISABLE_INSTRUMENTATIONS}` | ||
| ); | ||
@@ -142,5 +166,3 @@ } | ||
| disable.groups = parseEnvVar(process.env.INSTANA_TRACING_DISABLE_GROUPS); | ||
| logger?.info( | ||
| `Tracing instrumentation groups disabled via "INSTANA_TRACING_DISABLE_GROUPS": ${JSON.stringify(disable.groups)}` | ||
| ); | ||
| logger?.debug(`[config] env:INSTANA_TRACING_DISABLE_GROUPS = ${process.env.INSTANA_TRACING_DISABLE_GROUPS}`); | ||
| } | ||
@@ -147,0 +169,0 @@ |
@@ -28,3 +28,3 @@ /* | ||
| * Normalizes stack trace length configuration based on precedence. | ||
| * Precedence: global config > config > env var > default | ||
| * Precedence: env var > global config > config > default | ||
| * @param {import('../../config').InstanaConfig} config | ||
@@ -31,0 +31,0 @@ * @returns {number} - Normalized value |
+710
-409
@@ -6,14 +6,51 @@ /* | ||
| /* eslint-disable */ | ||
| 'use strict'; | ||
| const supportedTracingVersion = require('../tracing/supportedVersion'); | ||
| const configNormalizers = require('./configNormalizers'); | ||
| const configValidators = require('./configValidators'); | ||
| const deepMerge = require('../util/deepMerge'); | ||
| const { DEFAULT_STACK_TRACE_LENGTH, DEFAULT_STACK_TRACE_MODE } = require('../util/constants'); | ||
| const { DEFAULT_STACK_TRACE_LENGTH, DEFAULT_STACK_TRACE_MODE, CONFIG_SOURCES } = require('../util/constants'); | ||
| const { validateStackTraceMode, validateStackTraceLength } = require('./configValidators/stackTraceValidation'); | ||
| const util = require('./util'); | ||
| const validate = require('./validator'); | ||
| // @typedef {{ [x: string]: any }} configMeta | ||
| /** @type {configMeta} */ | ||
| const configMeta = {}; | ||
| const configStore = { | ||
| /** | ||
| * @param {string} configPath | ||
| * @param {{ source: number }} obj | ||
| */ | ||
| set(configPath, obj) { | ||
| configMeta[configPath] = obj; | ||
| }, | ||
| /** | ||
| * @param {string} configPath - The config path | ||
| * @returns {{ source: number } | undefined} | ||
| */ | ||
| get(configPath) { | ||
| return configMeta[configPath]; | ||
| }, | ||
| clear() { | ||
| Object.keys(configMeta).forEach(key => delete configMeta[key]); | ||
| } | ||
| }; | ||
| /** | ||
| * @type {InstanaConfig} | ||
| * | ||
| * NOTE: currentConfig is a reference to the config object returned by normalize(). | ||
| * This variable exists to allow dynamic config updates via the update() function without | ||
| * requiring the config object to be passed as a parameter. | ||
| * | ||
| * TODO: This can be removed in the future when we implement config.get()/config.set() | ||
| * methods. The values will be kept in the configStore instance. | ||
| */ | ||
| let currentConfig; | ||
| /** | ||
| * @typedef {Object} InstanaTracingOption | ||
@@ -147,2 +184,3 @@ * @property {boolean} [enabled] | ||
| configNormalizers.init({ logger }); | ||
| util.init(logger); | ||
| }; | ||
@@ -152,311 +190,429 @@ | ||
| * Merges the config that was passed to the init function with environment variables and default values. | ||
| */ | ||
| /** | ||
| * @param {InstanaConfig} [userConfig] | ||
| * @param {InstanaConfig} [defaultsOverride] | ||
| * @param {{ userConfig?: InstanaConfig, finalConfigBase?: Object, defaultsOverride?: InstanaConfig }} [options] | ||
| * @returns {InstanaConfig} | ||
| */ | ||
| module.exports.normalize = (userConfig, defaultsOverride = {}) => { | ||
| if (defaultsOverride && typeof defaultsOverride === 'object') { | ||
| module.exports.normalize = ({ userConfig = {}, finalConfigBase = {}, defaultsOverride = {} } = {}) => { | ||
| if (defaultsOverride && typeof defaultsOverride === 'object' && Object.keys(defaultsOverride).length > 0) { | ||
| defaults = deepMerge(defaults, defaultsOverride); | ||
| } | ||
| /** @type InstanaConfig */ | ||
| let targetConfig = {}; | ||
| let normalizedUserConfig; | ||
| // NOTE: Do not modify the original object | ||
| if (userConfig !== null) { | ||
| targetConfig = Object.assign({}, userConfig); | ||
| // NOTE: Do not modify the original user input object | ||
| if (userConfig !== null && userConfig !== undefined) { | ||
| normalizedUserConfig = Object.assign({}, userConfig); | ||
| } else { | ||
| normalizedUserConfig = {}; | ||
| } | ||
| // TODO: This call needs to be reconsidered when we add the full config instance (`config.get(...)`). | ||
| configStore.clear(); | ||
| // Preserve finalConfigBase in the finalConfig to allow additional config values | ||
| // that are not part of the core config schema. Eg: collector config needs to be preserved. | ||
| /** @type InstanaConfig */ | ||
| const finalConfig = finalConfigBase ? Object.assign({}, finalConfigBase) : {}; | ||
| // TODO: remove this and forward the logger via init fn. | ||
| targetConfig.logger = logger; | ||
| finalConfig.logger = logger; | ||
| normalizeServiceName(targetConfig); | ||
| normalizePackageJsonPath(targetConfig); | ||
| normalizeMetricsConfig(targetConfig); | ||
| normalizeTracingConfig(targetConfig); | ||
| normalizeSecrets(targetConfig); | ||
| normalizePreloadOpentelemetry(targetConfig); | ||
| return targetConfig; | ||
| normalizeServiceName({ userConfig: normalizedUserConfig, defaultConfig: defaults, finalConfig }); | ||
| normalizePackageJsonPath({ userConfig: normalizedUserConfig, defaultConfig: defaults, finalConfig }); | ||
| normalizeMetricsConfig({ userConfig: normalizedUserConfig, defaultConfig: defaults, finalConfig }); | ||
| normalizeTracingConfig({ userConfig: normalizedUserConfig, defaultConfig: defaults, finalConfig }); | ||
| normalizeSecrets({ userConfig: normalizedUserConfig, defaultConfig: defaults, finalConfig }); | ||
| normalizePreloadOpentelemetry({ userConfig: normalizedUserConfig, defaultConfig: defaults, finalConfig }); | ||
| currentConfig = finalConfig; | ||
| return finalConfig; | ||
| }; | ||
| /** | ||
| * @param {InstanaConfig} config | ||
| * @param {{ userConfig?: InstanaConfig|null, defaultConfig?: InstanaConfig, finalConfig?: InstanaConfig }} [options] | ||
| */ | ||
| function normalizeServiceName(config) { | ||
| if (config.serviceName == null && process.env['INSTANA_SERVICE_NAME']) { | ||
| config.serviceName = process.env['INSTANA_SERVICE_NAME']; | ||
| } | ||
| if (config.serviceName != null && typeof config.serviceName !== 'string') { | ||
| logger.warn( | ||
| `Invalid configuration: config.serviceName is not a string, the value will be ignored: ${config.serviceName}` | ||
| ); | ||
| config.serviceName = defaults.serviceName; | ||
| } | ||
| function normalizeServiceName({ userConfig = {}, defaultConfig = {}, finalConfig = {} } = {}) { | ||
| const { value, source } = util.resolve( | ||
| { | ||
| envValue: 'INSTANA_SERVICE_NAME', | ||
| inCodeValue: userConfig.serviceName, | ||
| defaultValue: defaultConfig.serviceName | ||
| }, | ||
| [validate.stringValidator] | ||
| ); | ||
| configStore.set('config.serviceName', { source }); | ||
| finalConfig.serviceName = value; | ||
| util.log({ configPath: 'config.serviceName', source, value, envVarName: 'INSTANA_SERVICE_NAME' }); | ||
| } | ||
| /** | ||
| * @param {InstanaConfig} config | ||
| * @param {{ userConfig?: InstanaConfig|null, defaultConfig?: InstanaConfig, finalConfig?: InstanaConfig }} [options] | ||
| */ | ||
| function normalizePackageJsonPath(config) { | ||
| if (config.packageJsonPath == null && process.env['INSTANA_PACKAGE_JSON_PATH']) { | ||
| config.packageJsonPath = process.env['INSTANA_PACKAGE_JSON_PATH']; | ||
| } | ||
| if (config.packageJsonPath != null && typeof config.packageJsonPath !== 'string') { | ||
| logger.warn( | ||
| `Invalid configuration: config.packageJsonPath is not a string, the value will be ignored: ${config.packageJsonPath}` | ||
| ); | ||
| function normalizePackageJsonPath({ userConfig = {}, defaultConfig = {}, finalConfig = {} } = {}) { | ||
| const { value, source } = util.resolve( | ||
| { | ||
| envValue: 'INSTANA_PACKAGE_JSON_PATH', | ||
| inCodeValue: userConfig.packageJsonPath, | ||
| defaultValue: defaultConfig.packageJsonPath | ||
| }, | ||
| [validate.stringValidator] | ||
| ); | ||
| config.packageJsonPath = null; | ||
| } | ||
| configStore.set('config.packageJsonPath', { source }); | ||
| finalConfig.packageJsonPath = value; | ||
| util.log({ | ||
| configPath: 'config.packageJsonPath', | ||
| source, | ||
| value, | ||
| envVarName: 'INSTANA_PACKAGE_JSON_PATH' | ||
| }); | ||
| } | ||
| /** | ||
| * @param {InstanaConfig} config | ||
| * @param {{ userConfig?: InstanaConfig|null, defaultConfig?: InstanaConfig, finalConfig?: InstanaConfig }} [options] | ||
| */ | ||
| function normalizeMetricsConfig(config) { | ||
| if (config.metrics == null) { | ||
| config.metrics = {}; | ||
| } | ||
| function normalizeMetricsConfig({ userConfig = {}, defaultConfig = {}, finalConfig = {} } = {}) { | ||
| const userMetrics = userConfig.metrics; | ||
| config.metrics.transmissionDelay = normalizeSingleValue( | ||
| config.metrics.transmissionDelay, | ||
| defaults.metrics.transmissionDelay, | ||
| 'config.metrics.transmissionDelay', | ||
| 'INSTANA_METRICS_TRANSMISSION_DELAY' | ||
| finalConfig.metrics = {}; | ||
| const { value: transmissionDelay, source: transmissionDelaySource } = util.resolve( | ||
| { | ||
| envValue: 'INSTANA_METRICS_TRANSMISSION_DELAY', | ||
| inCodeValue: userMetrics?.transmissionDelay, | ||
| defaultValue: defaultConfig.metrics.transmissionDelay | ||
| }, | ||
| [validate.numberValidator] | ||
| ); | ||
| finalConfig.metrics.transmissionDelay = transmissionDelay; | ||
| // Validate max value for transmissionDelay | ||
| if (config.metrics.transmissionDelay > transmissionDelayMaxValue) { | ||
| if (finalConfig.metrics.transmissionDelay > transmissionDelayMaxValue) { | ||
| logger.warn( | ||
| `The value of config.metrics.transmissionDelay (or INSTANA_METRICS_TRANSMISSION_DELAY) (${config.metrics.transmissionDelay}) exceeds the maximum allowed value of ${transmissionDelayMaxValue}. Assuming the max value ${transmissionDelayMaxValue}.` | ||
| // eslint-disable-next-line max-len | ||
| `The value of config.metrics.transmissionDelay (or INSTANA_METRICS_TRANSMISSION_DELAY) (${finalConfig.metrics.transmissionDelay}) exceeds the maximum allowed value of ${transmissionDelayMaxValue}. Assuming the max value ${transmissionDelayMaxValue}.` | ||
| ); | ||
| config.metrics.transmissionDelay = transmissionDelayMaxValue; | ||
| finalConfig.metrics.transmissionDelay = transmissionDelayMaxValue; | ||
| } | ||
| config.metrics.timeBetweenHealthcheckCalls = | ||
| config.metrics.timeBetweenHealthcheckCalls || defaults.metrics.timeBetweenHealthcheckCalls; | ||
| configStore.set('config.metrics.transmissionDelay', { source: transmissionDelaySource }); | ||
| util.log({ | ||
| configPath: 'config.metrics.transmissionDelay', | ||
| source: transmissionDelaySource, | ||
| value: transmissionDelay, | ||
| envVarName: 'INSTANA_METRICS_TRANSMISSION_DELAY' | ||
| }); | ||
| const { value: healthcheckInterval, source: healthcheckSource } = util.resolve( | ||
| { | ||
| inCodeValue: userMetrics?.timeBetweenHealthcheckCalls, | ||
| defaultValue: defaultConfig.metrics.timeBetweenHealthcheckCalls | ||
| }, | ||
| [validate.numberValidator] | ||
| ); | ||
| finalConfig.metrics.timeBetweenHealthcheckCalls = healthcheckInterval; | ||
| configStore.set('config.metrics.timeBetweenHealthcheckCalls', { | ||
| source: healthcheckSource | ||
| }); | ||
| util.log({ | ||
| configPath: 'config.metrics.timeBetweenHealthcheckCalls', | ||
| source: healthcheckSource, | ||
| value: healthcheckInterval | ||
| }); | ||
| } | ||
| /** | ||
| * | ||
| * @param {InstanaConfig} config | ||
| * @param {{ userConfig?: InstanaConfig|null, defaultConfig?: InstanaConfig, finalConfig?: InstanaConfig }} [options] | ||
| */ | ||
| function normalizeTracingConfig(config) { | ||
| if (config.tracing == null) { | ||
| config.tracing = {}; | ||
| } | ||
| normalizeTracingEnabled(config); | ||
| normalizeUseOpentelemetry(config); | ||
| normalizeDisableTracing(config); | ||
| normalizeAutomaticTracingEnabled(config); | ||
| normalizeActivateImmediately(config); | ||
| normalizeTracingTransmission(config); | ||
| normalizeTracingHttp(config); | ||
| normalizeTracingStackTrace(config); | ||
| normalizeSpanBatchingEnabled(config); | ||
| normalizeDisableW3cTraceCorrelation(config); | ||
| normalizeTracingKafka(config); | ||
| normalizeAllowRootExitSpan(config); | ||
| normalizeIgnoreEndpoints(config); | ||
| normalizeIgnoreEndpointsDisableSuppression(config); | ||
| normalizeDisableEOLEvents(config); | ||
| function normalizeTracingConfig({ userConfig = {}, defaultConfig = {}, finalConfig = {} } = {}) { | ||
| finalConfig.tracing = finalConfig.tracing || {}; | ||
| userConfig.tracing = userConfig.tracing || {}; | ||
| normalizeTracingEnabled({ userConfig, defaultConfig, finalConfig }); | ||
| normalizeUseOpentelemetry({ userConfig, defaultConfig, finalConfig }); | ||
| normalizeDisableTracing({ userConfig, defaultConfig, finalConfig }); | ||
| normalizeAutomaticTracingEnabled({ userConfig, defaultConfig, finalConfig }); | ||
| normalizeActivateImmediately({ userConfig, defaultConfig, finalConfig }); | ||
| normalizeTracingTransmission({ userConfig, defaultConfig, finalConfig }); | ||
| normalizeTracingHttp({ userConfig, defaultConfig, finalConfig }); | ||
| normalizeTracingStackTrace({ userConfig, defaultConfig, finalConfig }); | ||
| normalizeSpanBatchingEnabled({ userConfig, defaultConfig, finalConfig }); | ||
| normalizeDisableW3cTraceCorrelation({ userConfig, defaultConfig, finalConfig }); | ||
| normalizeTracingKafka({ userConfig, defaultConfig, finalConfig }); | ||
| normalizeAllowRootExitSpan({ userConfig, defaultConfig, finalConfig }); | ||
| normalizeIgnoreEndpoints({ userConfig, defaultConfig, finalConfig }); | ||
| normalizeIgnoreEndpointsDisableSuppression({ userConfig, defaultConfig, finalConfig }); | ||
| normalizeDisableEOLEvents({ userConfig, defaultConfig, finalConfig }); | ||
| } | ||
| /** | ||
| * | ||
| * @param {InstanaConfig} config | ||
| * @param {{ userConfig?: InstanaConfig|null, defaultConfig?: InstanaConfig, finalConfig?: InstanaConfig }} [options] | ||
| */ | ||
| function normalizeTracingEnabled(config) { | ||
| if (config.tracing.enabled === false) { | ||
| logger.info('Not enabling tracing as it is explicitly disabled via config.'); | ||
| return; | ||
| } | ||
| if (config.tracing.enabled === true) { | ||
| return; | ||
| } | ||
| function normalizeTracingEnabled({ userConfig = {}, defaultConfig = {}, finalConfig = {} } = {}) { | ||
| // INSTANA_TRACING_DISABLE can be either: | ||
| // 1. A boolean ('true'/'false') to enable/disable all tracing | ||
| // 2. A list of instrumentations/groups to selectively disable | ||
| // We only use it for tracing.enabled if it's a boolean value | ||
| const envValue = process.env.INSTANA_TRACING_DISABLE; | ||
| const isBooleanValue = envValue === 'true' || envValue === 'false'; | ||
| config.tracing.enabled = defaults.tracing.enabled; | ||
| const { value, source } = util.resolve( | ||
| { | ||
| envValue: isBooleanValue ? 'INSTANA_TRACING_DISABLE' : undefined, | ||
| inCodeValue: userConfig.tracing.enabled, | ||
| defaultValue: defaultConfig.tracing.enabled | ||
| }, | ||
| [validate.booleanValidator] | ||
| ); | ||
| // The env var is TRACING_DISABLE, so we need to invert it when it comes from env | ||
| // TODO: Consider adding this normalization support to util.resolver | ||
| const finalValue = source === CONFIG_SOURCES.ENV ? !value : value; | ||
| configStore.set('config.tracing.enabled', { source }); | ||
| finalConfig.tracing.enabled = finalValue; | ||
| util.log({ | ||
| configPath: 'config.tracing.enabled', | ||
| source, | ||
| value: finalValue, | ||
| envVarName: source === CONFIG_SOURCES.ENV ? 'INSTANA_TRACING_DISABLE' : undefined | ||
| }); | ||
| } | ||
| /** | ||
| * | ||
| * @param {InstanaConfig} config | ||
| * @param {{ userConfig?: InstanaConfig|null, defaultConfig?: InstanaConfig, finalConfig?: InstanaConfig }} [options] | ||
| */ | ||
| function normalizeAllowRootExitSpan({ userConfig = {}, defaultConfig = {}, finalConfig = {} } = {}) { | ||
| const { value, source } = util.resolve( | ||
| { | ||
| envValue: 'INSTANA_ALLOW_ROOT_EXIT_SPAN', | ||
| inCodeValue: userConfig.tracing.allowRootExitSpan, | ||
| defaultValue: defaultConfig.tracing.allowRootExitSpan | ||
| }, | ||
| [validate.booleanValidator] | ||
| ); | ||
| function normalizeAllowRootExitSpan(config) { | ||
| if (config.tracing.allowRootExitSpan === false) { | ||
| return; | ||
| } | ||
| if (config.tracing.allowRootExitSpan === true) { | ||
| return; | ||
| } | ||
| const INSTANA_ALLOW_ROOT_EXIT_SPAN = process.env['INSTANA_ALLOW_ROOT_EXIT_SPAN']?.toLowerCase(); | ||
| config.tracing.allowRootExitSpan = | ||
| INSTANA_ALLOW_ROOT_EXIT_SPAN === '1' || | ||
| INSTANA_ALLOW_ROOT_EXIT_SPAN === 'true' || | ||
| defaults.tracing.allowRootExitSpan; | ||
| return; | ||
| configStore.set('config.tracing.allowRootExitSpan', { source }); | ||
| finalConfig.tracing.allowRootExitSpan = value; | ||
| util.log({ | ||
| configPath: 'config.tracing.allowRootExitSpan', | ||
| source, | ||
| value, | ||
| envVarName: 'INSTANA_ALLOW_ROOT_EXIT_SPAN' | ||
| }); | ||
| } | ||
| /** | ||
| * | ||
| * @param {InstanaConfig} config | ||
| * @param {{ userConfig?: InstanaConfig|null, defaultConfig?: InstanaConfig, finalConfig?: InstanaConfig }} [options] | ||
| */ | ||
| function normalizeUseOpentelemetry(config) { | ||
| if (config.tracing.useOpentelemetry === false) { | ||
| return; | ||
| } | ||
| if (config.tracing.useOpentelemetry === true) { | ||
| return; | ||
| } | ||
| if (process.env['INSTANA_DISABLE_USE_OPENTELEMETRY'] === 'true') { | ||
| config.tracing.useOpentelemetry = false; | ||
| return; | ||
| } | ||
| function normalizeUseOpentelemetry({ userConfig = {}, defaultConfig = {}, finalConfig = {} } = {}) { | ||
| const { value, source } = util.resolve( | ||
| { | ||
| envValue: 'INSTANA_DISABLE_USE_OPENTELEMETRY', | ||
| inCodeValue: userConfig.tracing.useOpentelemetry, | ||
| defaultValue: defaultConfig.tracing.useOpentelemetry | ||
| }, | ||
| [validate.booleanValidator] | ||
| ); | ||
| config.tracing.useOpentelemetry = defaults.tracing.useOpentelemetry; | ||
| // The env var is DISABLE_USE_OPENTELEMETRY, so we need to invert it when it comes from env | ||
| // TODO: add normalization helpers to util.resolve(...) | ||
| const finalValue = source === CONFIG_SOURCES.ENV ? !value : value; | ||
| configStore.set('config.tracing.useOpentelemetry', { source }); | ||
| finalConfig.tracing.useOpentelemetry = finalValue; | ||
| util.log({ | ||
| configPath: 'config.tracing.useOpentelemetry', | ||
| source, | ||
| value: finalValue, | ||
| envVarName: source === CONFIG_SOURCES.ENV ? 'INSTANA_DISABLE_USE_OPENTELEMETRY' : undefined | ||
| }); | ||
| } | ||
| /** | ||
| * @param {InstanaConfig} config | ||
| * @param {{ userConfig?: InstanaConfig|null, defaultConfig?: InstanaConfig, finalConfig?: InstanaConfig }} [options] | ||
| */ | ||
| function normalizeAutomaticTracingEnabled(config) { | ||
| if (!config.tracing.enabled) { | ||
| logger.info('Not enabling automatic tracing as tracing in general is explicitly disabled via config.'); | ||
| config.tracing.automaticTracingEnabled = false; | ||
| function normalizeAutomaticTracingEnabled({ userConfig = {}, defaultConfig = {}, finalConfig = {} } = {}) { | ||
| if (!finalConfig.tracing.enabled) { | ||
| finalConfig.tracing.automaticTracingEnabled = false; | ||
| return; | ||
| } | ||
| if (config.tracing.automaticTracingEnabled === false) { | ||
| logger.info('Not enabling automatic tracing as it is explicitly disabled via config.'); | ||
| config.tracing.automaticTracingEnabled = false; | ||
| return; | ||
| } | ||
| const { value, source } = util.resolve( | ||
| { | ||
| envValue: 'INSTANA_DISABLE_AUTO_INSTR', | ||
| inCodeValue: userConfig.tracing.automaticTracingEnabled, | ||
| defaultValue: defaultConfig.tracing.automaticTracingEnabled | ||
| }, | ||
| [validate.booleanValidator] | ||
| ); | ||
| if (process.env['INSTANA_DISABLE_AUTO_INSTR'] === 'true') { | ||
| logger.info( | ||
| 'Not enabling automatic tracing as it is explicitly disabled via environment variable INSTANA_DISABLE_AUTO_INSTR.' | ||
| ); | ||
| config.tracing.automaticTracingEnabled = false; | ||
| return; | ||
| } | ||
| // The env var is DISABLE_AUTO_INSTR, so we need to invert it when it comes from env | ||
| // TODO: add normalization helpers to util.resolve(...) | ||
| const finalValue = source === CONFIG_SOURCES.ENV ? !value : value; | ||
| if (!supportedTracingVersion(process.versions.node)) { | ||
| logger.warn( | ||
| 'Not enabling automatic tracing, this is an unsupported version of Node.js. ' + | ||
| 'See: https://www.ibm.com/docs/en/instana-observability/current?topic=nodejs-support-information#supported-nodejs-versions' | ||
| ); | ||
| config.tracing.automaticTracingEnabled = false; | ||
| return; | ||
| } | ||
| config.tracing.automaticTracingEnabled = defaults.tracing.automaticTracingEnabled; | ||
| configStore.set('config.tracing.automaticTracingEnabled', { source }); | ||
| finalConfig.tracing.automaticTracingEnabled = finalValue; | ||
| util.log({ | ||
| configPath: 'config.tracing.automaticTracingEnabled', | ||
| source, | ||
| value: finalValue, | ||
| envVarName: source === CONFIG_SOURCES.ENV ? 'INSTANA_DISABLE_AUTO_INSTR' : undefined | ||
| }); | ||
| } | ||
| /** | ||
| * @param {InstanaConfig} config | ||
| * @param {{ userConfig?: InstanaConfig|null, defaultConfig?: InstanaConfig, finalConfig?: InstanaConfig }} [options] | ||
| */ | ||
| function normalizeActivateImmediately(config) { | ||
| if (!config.tracing.enabled) { | ||
| config.tracing.activateImmediately = false; | ||
| function normalizeActivateImmediately({ userConfig = {}, defaultConfig = {}, finalConfig = {} } = {}) { | ||
| if (!finalConfig.tracing.enabled) { | ||
| finalConfig.tracing.activateImmediately = false; | ||
| return; | ||
| } | ||
| if (typeof config.tracing.activateImmediately === 'boolean') { | ||
| return; | ||
| } | ||
| const { value, source } = util.resolve( | ||
| { | ||
| envValue: 'INSTANA_TRACE_IMMEDIATELY', | ||
| inCodeValue: userConfig.tracing.activateImmediately, | ||
| defaultValue: defaultConfig.tracing.activateImmediately | ||
| }, | ||
| [validate.booleanValidator] | ||
| ); | ||
| if (process.env['INSTANA_TRACE_IMMEDIATELY'] === 'true') { | ||
| config.tracing.activateImmediately = true; | ||
| return; | ||
| } | ||
| config.tracing.activateImmediately = defaults.tracing.activateImmediately; | ||
| configStore.set('config.tracing.activateImmediately', { source }); | ||
| finalConfig.tracing.activateImmediately = value; | ||
| util.log({ | ||
| configPath: 'config.tracing.activateImmediately', | ||
| source, | ||
| value, | ||
| envVarName: 'INSTANA_TRACE_IMMEDIATELY' | ||
| }); | ||
| } | ||
| /** | ||
| * @param {InstanaConfig} config | ||
| * @param {{ userConfig?: InstanaConfig|null, defaultConfig?: InstanaConfig, finalConfig?: InstanaConfig }} [options] | ||
| */ | ||
| function normalizeTracingTransmission(config) { | ||
| config.tracing.maxBufferedSpans = config.tracing.maxBufferedSpans || defaults.tracing.maxBufferedSpans; | ||
| function normalizeTracingTransmission({ userConfig = {}, defaultConfig = {}, finalConfig = {} } = {}) { | ||
| finalConfig.tracing.maxBufferedSpans = userConfig.tracing.maxBufferedSpans ?? defaultConfig.tracing.maxBufferedSpans; | ||
| config.tracing.transmissionDelay = normalizeSingleValue( | ||
| config.tracing.transmissionDelay, | ||
| defaults.tracing.transmissionDelay, | ||
| 'config.tracing.transmissionDelay', | ||
| 'INSTANA_TRACING_TRANSMISSION_DELAY' | ||
| const maxBufferedSpansSource = | ||
| userConfig.tracing.maxBufferedSpans !== undefined ? CONFIG_SOURCES.INCODE : CONFIG_SOURCES.DEFAULT; | ||
| configStore.set('config.tracing.maxBufferedSpans', { | ||
| source: maxBufferedSpansSource | ||
| }); | ||
| util.log({ | ||
| configPath: 'config.tracing.maxBufferedSpans', | ||
| source: maxBufferedSpansSource, | ||
| value: finalConfig.tracing.maxBufferedSpans | ||
| }); | ||
| const { value: tracingTransmissionDelay, source: tracingTransmissionDelaySource } = util.resolve( | ||
| { | ||
| envValue: 'INSTANA_TRACING_TRANSMISSION_DELAY', | ||
| inCodeValue: userConfig.tracing.transmissionDelay, | ||
| defaultValue: defaultConfig.tracing.transmissionDelay | ||
| }, | ||
| [validate.numberValidator] | ||
| ); | ||
| // DEPRECATED! This was never documented, but we shared it with a customer. | ||
| if (process.env['INSTANA_DEV_MIN_DELAY_BEFORE_SENDING_SPANS']) { | ||
| logger.warn( | ||
| 'The environment variable INSTANA_DEV_MIN_DELAY_BEFORE_SENDING_SPANS is deprecated and will be removed in the next major release. ' + | ||
| 'Please use INSTANA_TRACING_TRANSMISSION_DELAY instead.' | ||
| ); | ||
| configStore.set('config.tracing.transmissionDelay', { source: tracingTransmissionDelaySource }); | ||
| finalConfig.tracing.transmissionDelay = tracingTransmissionDelay; | ||
| util.log({ | ||
| configPath: 'config.tracing.transmissionDelay', | ||
| source: tracingTransmissionDelaySource, | ||
| value: tracingTransmissionDelay, | ||
| envVarName: 'INSTANA_TRACING_TRANSMISSION_DELAY' | ||
| }); | ||
| config.tracing.transmissionDelay = parseInt(process.env['INSTANA_DEV_MIN_DELAY_BEFORE_SENDING_SPANS'], 10); | ||
| const { value: forceTransmissionStartingAt, source: forceTransmissionStartingAtSource } = util.resolve( | ||
| { | ||
| envValue: 'INSTANA_FORCE_TRANSMISSION_STARTING_AT', | ||
| inCodeValue: userConfig.tracing.forceTransmissionStartingAt, | ||
| defaultValue: defaultConfig.tracing.forceTransmissionStartingAt | ||
| }, | ||
| [validate.numberValidator] | ||
| ); | ||
| if (isNaN(config.tracing.transmissionDelay)) { | ||
| logger.warn( | ||
| `The value of INSTANA_DEV_MIN_DELAY_BEFORE_SENDING_SPANS is not a number. Falling back to the default value ${defaults.tracing.transmissionDelay}.` | ||
| ); | ||
| configStore.set('config.tracing.forceTransmissionStartingAt', { source: forceTransmissionStartingAtSource }); | ||
| finalConfig.tracing.forceTransmissionStartingAt = forceTransmissionStartingAt; | ||
| util.log({ | ||
| configPath: 'config.tracing.forceTransmissionStartingAt', | ||
| source: forceTransmissionStartingAtSource, | ||
| value: forceTransmissionStartingAt, | ||
| envVarName: 'INSTANA_FORCE_TRANSMISSION_STARTING_AT' | ||
| }); | ||
| config.tracing.transmissionDelay = defaults.tracing.transmissionDelay; | ||
| } | ||
| } | ||
| config.tracing.forceTransmissionStartingAt = normalizeSingleValue( | ||
| config.tracing.forceTransmissionStartingAt, | ||
| defaults.tracing.forceTransmissionStartingAt, | ||
| 'config.tracing.forceTransmissionStartingAt', | ||
| 'INSTANA_FORCE_TRANSMISSION_STARTING_AT' | ||
| const { value: initialTransmissionDelay, source: initialTransmissionDelaySource } = util.resolve( | ||
| { | ||
| envValue: 'INSTANA_TRACING_INITIAL_TRANSMISSION_DELAY', | ||
| inCodeValue: userConfig.tracing.initialTransmissionDelay, | ||
| defaultValue: defaultConfig.tracing.initialTransmissionDelay | ||
| }, | ||
| [validate.numberValidator] | ||
| ); | ||
| config.tracing.initialTransmissionDelay = normalizeSingleValue( | ||
| config.tracing.initialTransmissionDelay, | ||
| defaults.tracing.initialTransmissionDelay, | ||
| 'config.tracing.initialTransmissionDelay', | ||
| 'INSTANA_TRACING_INITIAL_TRANSMISSION_DELAY' | ||
| ); | ||
| configStore.set('config.tracing.initialTransmissionDelay', { source: initialTransmissionDelaySource }); | ||
| finalConfig.tracing.initialTransmissionDelay = initialTransmissionDelay; | ||
| util.log({ | ||
| configPath: 'config.tracing.initialTransmissionDelay', | ||
| source: initialTransmissionDelaySource, | ||
| value: initialTransmissionDelay, | ||
| envVarName: 'INSTANA_TRACING_INITIAL_TRANSMISSION_DELAY' | ||
| }); | ||
| } | ||
| /** | ||
| * @param {InstanaConfig} config | ||
| * NOTE: This normalization logic is not handled in the resolver. | ||
| * because it involves complex multi-step processing: | ||
| * Future improvement: Consider refactoring to use a more generic resolver pattern. | ||
| * | ||
| * @param {{ userConfig?: InstanaConfig|null, defaultConfig?: InstanaConfig, finalConfig?: InstanaConfig }} [options] | ||
| */ | ||
| function normalizeTracingHttp(config) { | ||
| config.tracing.http = config.tracing.http || {}; | ||
| function normalizeTracingHttp({ userConfig = {}, defaultConfig = {}, finalConfig = {} } = {}) { | ||
| const userHttp = userConfig.tracing.http; | ||
| finalConfig.tracing.http = {}; | ||
| let fromEnvVar; | ||
| const userHeaders = userHttp?.extraHttpHeadersToCapture; | ||
| // 1. Check environment variable | ||
| if (process.env.INSTANA_EXTRA_HTTP_HEADERS) { | ||
| fromEnvVar = parseHeadersEnvVar(process.env.INSTANA_EXTRA_HTTP_HEADERS); | ||
| } | ||
| const fromEnvVar = parseHeadersEnvVar(process.env.INSTANA_EXTRA_HTTP_HEADERS); | ||
| finalConfig.tracing.http.extraHttpHeadersToCapture = fromEnvVar; | ||
| if (!config.tracing.http.extraHttpHeadersToCapture && !fromEnvVar) { | ||
| config.tracing.http.extraHttpHeadersToCapture = defaults.tracing.http.extraHttpHeadersToCapture; | ||
| configStore.set('config.tracing.http.extraHttpHeadersToCapture', { source: CONFIG_SOURCES.ENV }); | ||
| util.log({ | ||
| configPath: 'config.tracing.http.extraHttpHeadersToCapture', | ||
| source: CONFIG_SOURCES.ENV, | ||
| value: fromEnvVar, | ||
| envVarName: 'INSTANA_EXTRA_HTTP_HEADERS' | ||
| }); | ||
| return; | ||
| } else if (!config.tracing.http.extraHttpHeadersToCapture && fromEnvVar) { | ||
| config.tracing.http.extraHttpHeadersToCapture = fromEnvVar; | ||
| } | ||
| if (!Array.isArray(config.tracing.http.extraHttpHeadersToCapture)) { | ||
| logger.warn( | ||
| `Invalid configuration: config.tracing.http.extraHttpHeadersToCapture is not an array, the value will be ignored: ${JSON.stringify( | ||
| config.tracing.http.extraHttpHeadersToCapture | ||
| )}` | ||
| ); | ||
| config.tracing.http.extraHttpHeadersToCapture = defaults.tracing.http.extraHttpHeadersToCapture; | ||
| return; | ||
| // 2. Check in-code configuration | ||
| if (userHeaders !== undefined) { | ||
| if (!Array.isArray(userHeaders)) { | ||
| logger.warn( | ||
| // eslint-disable-next-line max-len | ||
| `Invalid configuration: config.tracing.http.extraHttpHeadersToCapture is not an array, the value will be ignored: ${JSON.stringify( | ||
| userHeaders | ||
| )}` | ||
| ); | ||
| } else { | ||
| finalConfig.tracing.http.extraHttpHeadersToCapture = userHeaders.map(s => s.toLowerCase()); | ||
| configStore.set('config.tracing.http.extraHttpHeadersToCapture', { source: CONFIG_SOURCES.INCODE }); | ||
| util.log({ | ||
| configPath: 'config.tracing.http.extraHttpHeadersToCapture', | ||
| source: CONFIG_SOURCES.INCODE, | ||
| value: finalConfig.tracing.http.extraHttpHeadersToCapture | ||
| }); | ||
| return; | ||
| } | ||
| } | ||
| config.tracing.http.extraHttpHeadersToCapture = config.tracing.http.extraHttpHeadersToCapture.map( | ||
| ( | ||
| s // Node.js HTTP API turns all incoming HTTP headers into lowercase. | ||
| ) => s.toLowerCase() | ||
| ); | ||
| // 3. Use default configuration | ||
| finalConfig.tracing.http.extraHttpHeadersToCapture = defaultConfig.tracing.http.extraHttpHeadersToCapture; | ||
| configStore.set('config.tracing.http.extraHttpHeadersToCapture', { source: CONFIG_SOURCES.DEFAULT }); | ||
| } | ||
| /** | ||
@@ -469,3 +625,3 @@ * @param {string} envVarValue | ||
| .split(/[;,]/) | ||
| .map(header => header.trim()) | ||
| .map(header => header.trim().toLowerCase()) | ||
| .filter(header => header !== ''); | ||
@@ -476,10 +632,18 @@ } | ||
| * Handles both stackTrace and stackTraceLength configuration | ||
| * @param {InstanaConfig} config | ||
| * | ||
| * NOTE: This normalization logic is not handled in the resolver. | ||
| * because it involves complex multi-step processing: | ||
| * Future improvement: Consider refactoring to use a more generic resolver pattern. | ||
| * | ||
| * | ||
| * @param {{ userConfig?: InstanaConfig|null, defaultConfig?: InstanaConfig, finalConfig?: InstanaConfig }} [options] | ||
| */ | ||
| function normalizeTracingStackTrace(config) { | ||
| const tracing = config.tracing; | ||
| function normalizeTracingStackTrace({ userConfig = {}, defaultConfig = {}, finalConfig = {} } = {}) { | ||
| const userTracingConfig = userConfig.tracing; | ||
| const userGlobal = userTracingConfig.global; | ||
| const envStackTrace = process.env['INSTANA_STACK_TRACE']; | ||
| const envStackTraceLength = process.env['INSTANA_STACK_TRACE_LENGTH']; | ||
| const envStackTrace = process.env.INSTANA_STACK_TRACE; | ||
| const envStackTraceLength = process.env.INSTANA_STACK_TRACE_LENGTH; | ||
| // Priority 1: Environment variable | ||
| if (envStackTrace !== undefined) { | ||
@@ -491,31 +655,40 @@ const result = validateStackTraceMode(envStackTrace); | ||
| if (normalized !== null) { | ||
| tracing.stackTrace = normalized; | ||
| finalConfig.tracing.stackTrace = normalized; | ||
| configStore.set('config.tracing.stackTrace', { source: CONFIG_SOURCES.ENV }); | ||
| } else { | ||
| tracing.stackTrace = defaults.tracing.stackTrace; | ||
| finalConfig.tracing.stackTrace = defaultConfig.tracing.stackTrace; | ||
| configStore.set('config.tracing.stackTrace', { source: CONFIG_SOURCES.DEFAULT }); | ||
| } | ||
| } else { | ||
| logger.warn(`Invalid env INSTANA_STACK_TRACE: ${result.error}`); | ||
| tracing.stackTrace = defaults.tracing.stackTrace; | ||
| finalConfig.tracing.stackTrace = defaultConfig.tracing.stackTrace; | ||
| configStore.set('config.tracing.stackTrace', { source: CONFIG_SOURCES.DEFAULT }); | ||
| } | ||
| } else if (tracing.global?.stackTrace !== undefined) { | ||
| const result = validateStackTraceMode(tracing.global.stackTrace); | ||
| } else if (userGlobal?.stackTrace !== undefined) { | ||
| // Priority 2: In-code configuration | ||
| const result = validateStackTraceMode(userGlobal.stackTrace); | ||
| if (result.isValid) { | ||
| const normalized = configNormalizers.stackTrace.normalizeStackTraceMode(config); | ||
| const normalized = configNormalizers.stackTrace.normalizeStackTraceMode(userConfig); | ||
| if (normalized !== null) { | ||
| tracing.stackTrace = normalized; | ||
| finalConfig.tracing.stackTrace = normalized; | ||
| configStore.set('config.tracing.stackTrace', { source: CONFIG_SOURCES.INCODE }); | ||
| } else { | ||
| tracing.stackTrace = defaults.tracing.stackTrace; | ||
| finalConfig.tracing.stackTrace = defaultConfig.tracing.stackTrace; | ||
| configStore.set('config.tracing.stackTrace', { source: CONFIG_SOURCES.DEFAULT }); | ||
| } | ||
| } else { | ||
| logger.warn(`Invalid config.tracing.global.stackTrace: ${result.error}`); | ||
| tracing.stackTrace = defaults.tracing.stackTrace; | ||
| finalConfig.tracing.stackTrace = defaultConfig.tracing.stackTrace; | ||
| configStore.set('config.tracing.stackTrace', { source: CONFIG_SOURCES.DEFAULT }); | ||
| } | ||
| } else { | ||
| tracing.stackTrace = defaults.tracing.stackTrace; | ||
| finalConfig.tracing.stackTrace = defaultConfig.tracing.stackTrace; | ||
| configStore.set('config.tracing.stackTrace', { source: CONFIG_SOURCES.DEFAULT }); | ||
| } | ||
| const isLegacyLengthDefined = tracing.stackTraceLength !== undefined; | ||
| const stackTraceConfigValue = tracing.global?.stackTraceLength || tracing.stackTraceLength; | ||
| const isLegacyLengthDefined = userTracingConfig?.stackTraceLength !== undefined; | ||
| const stackTraceConfigValue = userGlobal?.stackTraceLength || userTracingConfig?.stackTraceLength; | ||
| // Priority 1: Environment variable | ||
| if (envStackTraceLength !== undefined) { | ||
@@ -527,13 +700,18 @@ const result = validateStackTraceLength(envStackTraceLength); | ||
| if (normalized !== null) { | ||
| tracing.stackTraceLength = normalized; | ||
| finalConfig.tracing.stackTraceLength = normalized; | ||
| configStore.set('config.tracing.stackTraceLength', { source: CONFIG_SOURCES.ENV }); | ||
| } else { | ||
| tracing.stackTraceLength = defaults.tracing.stackTraceLength; | ||
| finalConfig.tracing.stackTraceLength = defaultConfig.tracing.stackTraceLength; | ||
| configStore.set('config.tracing.stackTraceLength', { source: CONFIG_SOURCES.DEFAULT }); | ||
| } | ||
| } else { | ||
| logger.warn(`Invalid env INSTANA_STACK_TRACE_LENGTH: ${result.error}`); | ||
| tracing.stackTraceLength = defaults.tracing.stackTraceLength; | ||
| finalConfig.tracing.stackTraceLength = defaultConfig.tracing.stackTraceLength; | ||
| configStore.set('config.tracing.stackTraceLength', { source: CONFIG_SOURCES.DEFAULT }); | ||
| } | ||
| } else if (stackTraceConfigValue !== undefined) { | ||
| // Priority 2: In-code configuration | ||
| if (isLegacyLengthDefined) { | ||
| logger.warn( | ||
| // eslint-disable-next-line max-len | ||
| '[Deprecation Warning] The configuration option config.tracing.stackTraceLength is deprecated and will be removed in a future release. ' + | ||
@@ -547,14 +725,18 @@ 'Please use config.tracing.global.stackTraceLength instead.' | ||
| if (result.isValid) { | ||
| const normalized = configNormalizers.stackTrace.normalizeStackTraceLength(config); | ||
| const normalized = configNormalizers.stackTrace.normalizeStackTraceLength(userConfig); | ||
| if (normalized !== null) { | ||
| tracing.stackTraceLength = normalized; | ||
| finalConfig.tracing.stackTraceLength = normalized; | ||
| configStore.set('config.tracing.stackTraceLength', { source: CONFIG_SOURCES.INCODE }); | ||
| } else { | ||
| tracing.stackTraceLength = defaults.tracing.stackTraceLength; | ||
| finalConfig.tracing.stackTraceLength = defaultConfig.tracing.stackTraceLength; | ||
| configStore.set('config.tracing.stackTraceLength', { source: CONFIG_SOURCES.DEFAULT }); | ||
| } | ||
| } else { | ||
| logger.warn(`Invalid stackTraceLength value: ${result.error}`); | ||
| tracing.stackTraceLength = defaults.tracing.stackTraceLength; | ||
| finalConfig.tracing.stackTraceLength = defaultConfig.tracing.stackTraceLength; | ||
| configStore.set('config.tracing.stackTraceLength', { source: CONFIG_SOURCES.DEFAULT }); | ||
| } | ||
| } else { | ||
| tracing.stackTraceLength = defaults.tracing.stackTraceLength; | ||
| finalConfig.tracing.stackTraceLength = defaultConfig.tracing.stackTraceLength; | ||
| configStore.set('config.tracing.stackTraceLength', { source: CONFIG_SOURCES.DEFAULT }); | ||
| } | ||
@@ -564,6 +746,11 @@ } | ||
| /** | ||
| * @param {InstanaConfig} config | ||
| * NOTE: This normalization logic is not handled in the resolver. | ||
| * because it involves complex multi-step processing: | ||
| * Future improvement: Consider refactoring to use a more generic resolver pattern. | ||
| * | ||
| * @param {{ userConfig?: InstanaConfig|null, defaultConfig?: InstanaConfig, finalConfig?: InstanaConfig }} [options] | ||
| */ | ||
| function normalizeDisableTracing(config) { | ||
| const disableConfig = configNormalizers.disable.normalize(config); | ||
| function normalizeDisableTracing({ userConfig = {}, defaultConfig = {}, finalConfig = {} } = {}) { | ||
| const disableRes = configNormalizers.disable.normalize(userConfig); | ||
| const disableConfig = disableRes?.value; | ||
@@ -573,87 +760,104 @@ // If tracing is globally disabled (via `disable: true` or INSTANA_TRACING_DISABLE=true ), | ||
| if (disableConfig === true) { | ||
| config.tracing.enabled = false; | ||
| config.tracing.disable = {}; | ||
| finalConfig.tracing.enabled = false; | ||
| finalConfig.tracing.disable = {}; | ||
| configStore.set('config.tracing.disable', { | ||
| source: disableRes.source | ||
| }); | ||
| return; | ||
| } | ||
| if (typeof disableConfig === 'object' && (disableConfig.instrumentations?.length || disableConfig.groups?.length)) { | ||
| config.tracing.disable = disableConfig; | ||
| if (typeof disableConfig === 'object' && disableRes.source !== CONFIG_SOURCES.DEFAULT) { | ||
| finalConfig.tracing.disable = disableConfig; | ||
| configStore.set('config.tracing.disable', { | ||
| source: disableRes.source | ||
| }); | ||
| return; | ||
| } | ||
| config.tracing.disable = defaults.tracing.disable; | ||
| finalConfig.tracing.disable = defaultConfig.tracing.disable; | ||
| configStore.set('config.tracing.disable', { source: CONFIG_SOURCES.DEFAULT }); | ||
| } | ||
| /** | ||
| * @param {InstanaConfig} config | ||
| * @param {{ userConfig?: InstanaConfig|null, defaultConfig?: InstanaConfig, finalConfig?: InstanaConfig }} [options] | ||
| */ | ||
| function normalizeSpanBatchingEnabled(config) { | ||
| if (config.tracing.spanBatchingEnabled != null) { | ||
| if (typeof config.tracing.spanBatchingEnabled === 'boolean') { | ||
| if (config.tracing.spanBatchingEnabled) { | ||
| logger.info('Span batching is enabled via config.'); | ||
| } | ||
| return; | ||
| } else { | ||
| logger.warn( | ||
| `Invalid configuration: config.tracing.spanBatchingEnabled is not a boolean value, will be ignored: ${JSON.stringify( | ||
| config.tracing.spanBatchingEnabled | ||
| )}` | ||
| ); | ||
| } | ||
| } | ||
| function normalizeSpanBatchingEnabled({ userConfig = {}, defaultConfig = {}, finalConfig = {} } = {}) { | ||
| const { value, source } = util.resolve( | ||
| { | ||
| envValue: 'INSTANA_SPANBATCHING_ENABLED', | ||
| inCodeValue: userConfig.tracing.spanBatchingEnabled, | ||
| defaultValue: defaultConfig.tracing.spanBatchingEnabled | ||
| }, | ||
| [validate.booleanValidator] | ||
| ); | ||
| if (process.env['INSTANA_SPANBATCHING_ENABLED'] === 'true') { | ||
| logger.info('Span batching is enabled via environment variable INSTANA_SPANBATCHING_ENABLED.'); | ||
| config.tracing.spanBatchingEnabled = true; | ||
| return; | ||
| } | ||
| config.tracing.spanBatchingEnabled = defaults.tracing.spanBatchingEnabled; | ||
| configStore.set('config.tracing.spanBatchingEnabled', { source }); | ||
| finalConfig.tracing.spanBatchingEnabled = value; | ||
| util.log({ | ||
| configPath: 'config.tracing.spanBatchingEnabled', | ||
| source, | ||
| value, | ||
| envVarName: 'INSTANA_SPANBATCHING_ENABLED' | ||
| }); | ||
| } | ||
| /** | ||
| * @param {InstanaConfig} config | ||
| * @param {{ userConfig?: InstanaConfig|null, defaultConfig?: InstanaConfig, finalConfig?: InstanaConfig }} [options] | ||
| */ | ||
| function normalizeDisableW3cTraceCorrelation(config) { | ||
| if (config.tracing.disableW3cTraceCorrelation === true) { | ||
| logger.info('W3C trace correlation has been disabled via config.'); | ||
| return; | ||
| } | ||
| if (process.env['INSTANA_DISABLE_W3C_TRACE_CORRELATION']) { | ||
| logger.info( | ||
| 'W3C trace correlation has been disabled via environment variable INSTANA_DISABLE_W3C_TRACE_CORRELATION.' | ||
| ); | ||
| config.tracing.disableW3cTraceCorrelation = true; | ||
| return; | ||
| } | ||
| function normalizeDisableW3cTraceCorrelation({ userConfig = {}, defaultConfig = {}, finalConfig = {} } = {}) { | ||
| const { value, source } = util.resolve( | ||
| { | ||
| envValue: 'INSTANA_DISABLE_W3C_TRACE_CORRELATION', | ||
| inCodeValue: userConfig.tracing.disableW3cTraceCorrelation, | ||
| defaultValue: defaultConfig.tracing.disableW3cTraceCorrelation | ||
| }, | ||
| [validate.validateTruthyBoolean] | ||
| ); | ||
| config.tracing.disableW3cTraceCorrelation = defaults.tracing.disableW3cTraceCorrelation; | ||
| configStore.set('config.tracing.disableW3cTraceCorrelation', { source }); | ||
| finalConfig.tracing.disableW3cTraceCorrelation = value; | ||
| util.log({ | ||
| configPath: 'config.tracing.disableW3cTraceCorrelation', | ||
| source, | ||
| value, | ||
| envVarName: 'INSTANA_DISABLE_W3C_TRACE_CORRELATION' | ||
| }); | ||
| } | ||
| /** | ||
| * @param {InstanaConfig} config | ||
| * @param {{ userConfig?: InstanaConfig|null, defaultConfig?: InstanaConfig, finalConfig?: InstanaConfig }} [options] | ||
| */ | ||
| function normalizeTracingKafka(config) { | ||
| config.tracing.kafka = config.tracing.kafka || {}; | ||
| function normalizeTracingKafka({ userConfig = {}, defaultConfig = {}, finalConfig = {} } = {}) { | ||
| const userKafka = userConfig.tracing.kafka || {}; | ||
| if (config.tracing.kafka.traceCorrelation === false) { | ||
| logger.info('Kafka trace correlation has been disabled via config.'); | ||
| } else if ( | ||
| process.env['INSTANA_KAFKA_TRACE_CORRELATION'] != null && | ||
| process.env['INSTANA_KAFKA_TRACE_CORRELATION'].toLowerCase() === 'false' | ||
| ) { | ||
| logger.info('Kafka trace correlation has been disabled via environment variable INSTANA_KAFKA_TRACE_CORRELATION.'); | ||
| config.tracing.kafka.traceCorrelation = false; | ||
| } else { | ||
| config.tracing.kafka.traceCorrelation = defaults.tracing.kafka.traceCorrelation; | ||
| } | ||
| finalConfig.tracing.kafka = finalConfig.tracing.kafka || {}; | ||
| const { value, source } = util.resolve( | ||
| { | ||
| envValue: 'INSTANA_KAFKA_TRACE_CORRELATION', | ||
| inCodeValue: userKafka.traceCorrelation, | ||
| defaultValue: defaultConfig.tracing.kafka.traceCorrelation | ||
| }, | ||
| [validate.booleanValidator] | ||
| ); | ||
| configStore.set('config.tracing.kafka.traceCorrelation', { source }); | ||
| finalConfig.tracing.kafka.traceCorrelation = value; | ||
| util.log({ | ||
| configPath: 'config.tracing.kafka.traceCorrelation', | ||
| source, | ||
| value, | ||
| envVarName: 'INSTANA_KAFKA_TRACE_CORRELATION' | ||
| }); | ||
| } | ||
| /** | ||
| * @param {InstanaConfig} config | ||
| * NOTE: This normalization logic is not handled in the resolver. | ||
| * because it involves complex multi-step processing: | ||
| * Future improvement: Consider refactoring to use a more generic resolver pattern. | ||
| * | ||
| * @param {{ userConfig?: InstanaConfig|null, defaultConfig?: InstanaConfig, finalConfig?: InstanaConfig }} [options] | ||
| */ | ||
| function normalizeSecrets(config) { | ||
| if (config.secrets == null) { | ||
| config.secrets = {}; | ||
| } | ||
| function normalizeSecrets({ userConfig = {}, defaultConfig = {}, finalConfig = {} } = {}) { | ||
| const userSecrets = userConfig.secrets; | ||
| finalConfig.secrets = {}; | ||
@@ -666,23 +870,68 @@ /** @type {InstanaSecretsOption} */ | ||
| config.secrets.matcherMode = config.secrets.matcherMode || fromEnvVar.matcherMode || defaults.secrets.matcherMode; | ||
| config.secrets.keywords = config.secrets.keywords || fromEnvVar.keywords || defaults.secrets.keywords; | ||
| let matcherModeSource; | ||
| let matcherMode; | ||
| if (typeof config.secrets.matcherMode !== 'string') { | ||
| if (fromEnvVar.matcherMode) { | ||
| matcherMode = fromEnvVar.matcherMode; | ||
| matcherModeSource = CONFIG_SOURCES.ENV; | ||
| logger.debug(`[config] env:INSTANA_SECRETS (matcherMode) = ${matcherMode}`); | ||
| } else if (userSecrets?.matcherMode) { | ||
| matcherMode = userSecrets.matcherMode; | ||
| matcherModeSource = CONFIG_SOURCES.INCODE; | ||
| logger.debug(`[config] incode:config.secrets.matcherMode = ${matcherMode}`); | ||
| } else { | ||
| matcherMode = defaultConfig.secrets.matcherMode; | ||
| matcherModeSource = CONFIG_SOURCES.DEFAULT; | ||
| } | ||
| let keywordsSource; | ||
| let keywords; | ||
| if (fromEnvVar.keywords) { | ||
| keywords = fromEnvVar.keywords; | ||
| keywordsSource = CONFIG_SOURCES.ENV; | ||
| logger.debug('[config] env:INSTANA_SECRETS (keywords)'); | ||
| } else if (userSecrets?.keywords) { | ||
| keywords = userSecrets.keywords; | ||
| keywordsSource = CONFIG_SOURCES.INCODE; | ||
| logger.debug('[config] incode:config.secrets.keywords'); | ||
| } else { | ||
| keywords = defaultConfig.secrets.keywords; | ||
| keywordsSource = CONFIG_SOURCES.DEFAULT; | ||
| } | ||
| if (typeof matcherMode !== 'string') { | ||
| logger.warn( | ||
| `The value of config.secrets.matcherMode ("${config.secrets.matcherMode}") is not a string. Assuming the default value ${defaults.secrets.matcherMode}.` | ||
| // eslint-disable-next-line max-len | ||
| `The value of config.secrets.matcherMode ("${matcherMode}") is not a string. Assuming the default value ${defaults.secrets.matcherMode}.` | ||
| ); | ||
| config.secrets.matcherMode = defaults.secrets.matcherMode; | ||
| } else if (validSecretsMatcherModes.indexOf(config.secrets.matcherMode) < 0) { | ||
| finalConfig.secrets.matcherMode = defaultConfig.secrets.matcherMode; | ||
| configStore.set('config.secrets.matcherMode', { source: CONFIG_SOURCES.DEFAULT }); | ||
| } else if (validSecretsMatcherModes.indexOf(matcherMode) < 0) { | ||
| logger.warn( | ||
| `The value of config.secrets.matcherMode (or the matcher mode parsed from INSTANA_SECRETS) (${config.secrets.matcherMode}) is not a supported matcher mode. Assuming the default value ${defaults.secrets.matcherMode}.` | ||
| // eslint-disable-next-line max-len | ||
| `The value of config.secrets.matcherMode (or the matcher mode parsed from INSTANA_SECRETS) (${matcherMode}) is not a supported matcher mode. Assuming the default value ${defaults.secrets.matcherMode}.` | ||
| ); | ||
| config.secrets.matcherMode = defaults.secrets.matcherMode; | ||
| } else if (!Array.isArray(config.secrets.keywords)) { | ||
| finalConfig.secrets.matcherMode = defaultConfig.secrets.matcherMode; | ||
| configStore.set('config.secrets.matcherMode', { source: CONFIG_SOURCES.DEFAULT }); | ||
| } else { | ||
| finalConfig.secrets.matcherMode = matcherMode; | ||
| configStore.set('config.secrets.matcherMode', { source: matcherModeSource }); | ||
| } | ||
| if (!Array.isArray(keywords)) { | ||
| logger.warn( | ||
| `The value of config.secrets.keywords (${config.secrets.keywords}) is not an array. Assuming the default value ${defaults.secrets.keywords}.` | ||
| // eslint-disable-next-line max-len | ||
| `The value of config.secrets.keywords (${keywords}) is not an array. Assuming the default value ${defaults.secrets.keywords}.` | ||
| ); | ||
| config.secrets.keywords = defaults.secrets.keywords; | ||
| finalConfig.secrets.keywords = defaultConfig.secrets.keywords; | ||
| configStore.set('config.secrets.keywords', { source: CONFIG_SOURCES.DEFAULT }); | ||
| } else { | ||
| finalConfig.secrets.keywords = keywords; | ||
| configStore.set('config.secrets.keywords', { source: keywordsSource }); | ||
| } | ||
| if (config.secrets.matcherMode === 'none') { | ||
| config.secrets.keywords = []; | ||
| if (finalConfig.secrets.matcherMode === 'none') { | ||
| finalConfig.secrets.keywords = []; | ||
| configStore.set('config.secrets.keywords', { source: matcherModeSource }); | ||
| } | ||
@@ -711,3 +960,3 @@ } | ||
| function parseSecretsEnvVar(envVarValue) { | ||
| let [matcherMode, keywords] = envVarValue.split(':', 2); | ||
| const [matcherMode, keywords] = envVarValue.split(':', 2); | ||
@@ -726,2 +975,3 @@ const parsedMatcherMode = parseMatcherMode(matcherMode); | ||
| logger.warn( | ||
| // eslint-disable-next-line max-len | ||
| `The value of INSTANA_SECRETS (${envVarValue}) cannot be parsed. Please use the following format: INSTANA_SECRETS=<matcher>:<secret>[,<secret>]. This setting will be ignored.` | ||
@@ -738,118 +988,169 @@ ); | ||
| } | ||
| /** | ||
| * @param {*} configValue | ||
| * @param {*} defaultValue | ||
| * @param {string} configPath | ||
| * @param {string} envVarKey | ||
| * @returns {*} | ||
| * NOTE: This normalization logic is not handled in the resolver. | ||
| * because it involves complex multi-step processing: | ||
| * Future improvement: Consider refactoring to use a more generic resolver pattern. | ||
| * | ||
| * @param {{ userConfig?: InstanaConfig|null, defaultConfig?: InstanaConfig, finalConfig?: InstanaConfig }} [options] | ||
| */ | ||
| 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 normalizeIgnoreEndpoints({ userConfig = {}, defaultConfig = {}, finalConfig = {} } = {}) { | ||
| const userIgnoreEndpoints = userConfig.tracing.ignoreEndpoints; | ||
| // Priority 1: Load from a YAML file if `INSTANA_IGNORE_ENDPOINTS_PATH` is set | ||
| // Introduced in Phase 2 for advanced filtering based on both methods and endpoints. | ||
| // Also supports basic filtering for endpoints. | ||
| if (process.env.INSTANA_IGNORE_ENDPOINTS_PATH) { | ||
| finalConfig.tracing.ignoreEndpoints = configNormalizers.ignoreEndpoints.fromYaml( | ||
| process.env.INSTANA_IGNORE_ENDPOINTS_PATH | ||
| ); | ||
| logger.debug('[config] env:INSTANA_IGNORE_ENDPOINTS_PATH'); | ||
| configStore.set('config.tracing.ignoreEndpoints', { source: CONFIG_SOURCES.ENV }); | ||
| return; | ||
| } | ||
| 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}.` | ||
| // Priority 2: Load from the `INSTANA_IGNORE_ENDPOINTS` environment variable | ||
| // Introduced in Phase 1 for basic filtering based only on operations (e.g., `redis.get`, `kafka.consume`). | ||
| // Provides a simple way to configure ignored operations via environment variables. | ||
| if (process.env.INSTANA_IGNORE_ENDPOINTS) { | ||
| finalConfig.tracing.ignoreEndpoints = configNormalizers.ignoreEndpoints.fromEnv( | ||
| process.env.INSTANA_IGNORE_ENDPOINTS | ||
| ); | ||
| return defaultValue; | ||
| logger.debug('[config] env:INSTANA_IGNORE_ENDPOINTS'); | ||
| configStore.set('config.tracing.ignoreEndpoints', { source: CONFIG_SOURCES.ENV }); | ||
| return; | ||
| } | ||
| return configValue; | ||
| } | ||
| /** | ||
| * @param {InstanaConfig} config | ||
| */ | ||
| function normalizeIgnoreEndpoints(config) { | ||
| if (!config.tracing.ignoreEndpoints) { | ||
| config.tracing.ignoreEndpoints = {}; | ||
| } | ||
| const ignoreEndpointsConfig = config.tracing.ignoreEndpoints; | ||
| if (typeof ignoreEndpointsConfig !== 'object' || Array.isArray(ignoreEndpointsConfig)) { | ||
| // Priority 3: Use in-code configuration if available | ||
| if (userIgnoreEndpoints && (typeof userIgnoreEndpoints !== 'object' || Array.isArray(userIgnoreEndpoints))) { | ||
| logger.warn( | ||
| `Invalid tracing.ignoreEndpoints configuration. Expected an object, but received: ${JSON.stringify( | ||
| ignoreEndpointsConfig | ||
| userIgnoreEndpoints | ||
| )}` | ||
| ); | ||
| config.tracing.ignoreEndpoints = {}; | ||
| finalConfig.tracing.ignoreEndpoints = defaultConfig.tracing.ignoreEndpoints; | ||
| configStore.set('config.tracing.ignoreEndpoints', { source: CONFIG_SOURCES.DEFAULT }); | ||
| return; | ||
| } | ||
| // Case 1: Use in-code configuration if available | ||
| if (Object.keys(ignoreEndpointsConfig).length) { | ||
| config.tracing.ignoreEndpoints = configNormalizers.ignoreEndpoints.normalizeConfig(ignoreEndpointsConfig); | ||
| logger.debug(`Ignore endpoints have been configured: ${JSON.stringify(config.tracing.ignoreEndpoints)}`); | ||
| if (userIgnoreEndpoints && Object.keys(userIgnoreEndpoints).length) { | ||
| finalConfig.tracing.ignoreEndpoints = configNormalizers.ignoreEndpoints.normalizeConfig(userIgnoreEndpoints); | ||
| logger.debug('[config] incode:config.tracing.ignoreEndpoints'); | ||
| configStore.set('config.tracing.ignoreEndpoints', { source: CONFIG_SOURCES.INCODE }); | ||
| return; | ||
| } | ||
| // Case 2: Load from a YAML file if `INSTANA_IGNORE_ENDPOINTS_PATH` is set | ||
| // Introduced in Phase 2 for advanced filtering based on both methods and endpoints. | ||
| // Also supports basic filtering for endpoints. | ||
| if (process.env.INSTANA_IGNORE_ENDPOINTS_PATH) { | ||
| config.tracing.ignoreEndpoints = configNormalizers.ignoreEndpoints.fromYaml( | ||
| process.env.INSTANA_IGNORE_ENDPOINTS_PATH | ||
| ); | ||
| finalConfig.tracing.ignoreEndpoints = defaultConfig.tracing.ignoreEndpoints; | ||
| configStore.set('config.tracing.ignoreEndpoints', { source: CONFIG_SOURCES.DEFAULT }); | ||
| } | ||
| logger.debug(`Ignore endpoints have been configured: ${JSON.stringify(config.tracing.ignoreEndpoints)}`); | ||
| return; | ||
| } | ||
| /** | ||
| * @param {{ userConfig?: InstanaConfig|null, defaultConfig?: InstanaConfig, finalConfig?: InstanaConfig }} [options] | ||
| */ | ||
| function normalizeIgnoreEndpointsDisableSuppression({ userConfig = {}, defaultConfig = {}, finalConfig = {} } = {}) { | ||
| const { value, source } = util.resolve( | ||
| { | ||
| envValue: 'INSTANA_IGNORE_ENDPOINTS_DISABLE_SUPPRESSION', | ||
| inCodeValue: userConfig.tracing.ignoreEndpointsDisableSuppression, | ||
| defaultValue: defaultConfig.tracing.ignoreEndpointsDisableSuppression | ||
| }, | ||
| [validate.booleanValidator] | ||
| ); | ||
| // Case 3: Load from the `INSTANA_IGNORE_ENDPOINTS` environment variable | ||
| // Introduced in Phase 1 for basic filtering based only on operations (e.g., `redis.get`, `kafka.consume`). | ||
| // Provides a simple way to configure ignored operations via environment variables. | ||
| if (process.env.INSTANA_IGNORE_ENDPOINTS) { | ||
| config.tracing.ignoreEndpoints = configNormalizers.ignoreEndpoints.fromEnv(process.env.INSTANA_IGNORE_ENDPOINTS); | ||
| logger.debug(`Ignore endpoints have been configured: ${JSON.stringify(config.tracing.ignoreEndpoints)}`); | ||
| return; | ||
| } | ||
| configStore.set('config.tracing.ignoreEndpointsDisableSuppression', { source }); | ||
| finalConfig.tracing.ignoreEndpointsDisableSuppression = value; | ||
| util.log({ | ||
| configPath: 'config.tracing.ignoreEndpointsDisableSuppression', | ||
| source, | ||
| value, | ||
| envVarName: 'INSTANA_IGNORE_ENDPOINTS_DISABLE_SUPPRESSION' | ||
| }); | ||
| } | ||
| /** | ||
| * @param {InstanaConfig} config | ||
| * @param {{ userConfig?: InstanaConfig|null, defaultConfig?: InstanaConfig, finalConfig?: InstanaConfig }} [options] | ||
| */ | ||
| function normalizeIgnoreEndpointsDisableSuppression(config) { | ||
| if (process.env['INSTANA_IGNORE_ENDPOINTS_DISABLE_SUPPRESSION'] === 'true') { | ||
| logger.info( | ||
| 'Disabling downstream suppression for ignoring endpoints feature as it is explicitly disabled via environment variable "INSTANA_IGNORE_ENDPOINTS_DISABLE_SUPPRESSION".' | ||
| ); | ||
| config.tracing.ignoreEndpointsDisableSuppression = true; | ||
| return; | ||
| } | ||
| function normalizeDisableEOLEvents({ userConfig = {}, defaultConfig = {}, finalConfig = {} } = {}) { | ||
| const { value, source } = util.resolve( | ||
| { | ||
| envValue: 'INSTANA_TRACING_DISABLE_EOL_EVENTS', | ||
| inCodeValue: userConfig.tracing.disableEOLEvents, | ||
| defaultValue: defaultConfig.tracing.disableEOLEvents | ||
| }, | ||
| [validate.booleanValidator] | ||
| ); | ||
| config.tracing.ignoreEndpointsDisableSuppression = defaults.tracing.ignoreEndpointsDisableSuppression; | ||
| configStore.set('config.tracing.disableEOLEvents', { source }); | ||
| finalConfig.tracing.disableEOLEvents = value; | ||
| util.log({ | ||
| configPath: 'config.tracing.disableEOLEvents', | ||
| source, | ||
| value, | ||
| envVarName: 'INSTANA_TRACING_DISABLE_EOL_EVENTS' | ||
| }); | ||
| } | ||
| /** | ||
| * @param {InstanaConfig} config | ||
| * @param {{ userConfig?: InstanaConfig|null, defaultConfig?: InstanaConfig, finalConfig?: InstanaConfig }} [options] | ||
| */ | ||
| function normalizeDisableEOLEvents(config) { | ||
| config.tracing = config.tracing || {}; | ||
| function normalizePreloadOpentelemetry({ userConfig = {}, defaultConfig = {}, finalConfig = {} } = {}) { | ||
| const { value, source } = util.resolve( | ||
| { | ||
| inCodeValue: userConfig.preloadOpentelemetry, | ||
| defaultValue: defaultConfig.preloadOpentelemetry | ||
| }, | ||
| [validate.booleanValidator] | ||
| ); | ||
| if (process.env['INSTANA_TRACING_DISABLE_EOL_EVENTS'] === 'true') { | ||
| logger.info( | ||
| 'Disabling EOL events as it is explicitly disabled via environment variable "INSTANA_TRACING_DISABLE_EOL_EVENTS".' | ||
| ); | ||
| config.tracing.disableEOLEvents = true; | ||
| return; | ||
| } | ||
| config.tracing.disableEOLEvents = defaults.tracing.disableEOLEvents; | ||
| finalConfig.preloadOpentelemetry = value; | ||
| configStore.set('config.preloadOpentelemetry', { source }); | ||
| util.log({ | ||
| configPath: 'config.preloadOpentelemetry', | ||
| source, | ||
| value | ||
| }); | ||
| } | ||
| /** | ||
| * @param {InstanaConfig} config | ||
| * Updates configuration values dynamically from external sources (e.g., agent) | ||
| * | ||
| * @param {Object} [params] | ||
| * @param {Record<string, any>} [params.externalConfig] | ||
| * @param {number} [params.source] | ||
| * @param {Record<string, any>} [params.target=currentConfig] | ||
| * @param {string} [params.basePath='config'] | ||
| * @returns {Record<string, any>} | ||
| */ | ||
| function normalizePreloadOpentelemetry(config) { | ||
| if (config.preloadOpentelemetry === true) { | ||
| return; | ||
| exports.update = function update({ externalConfig = {}, source, target = currentConfig, basePath = 'config' } = {}) { | ||
| if (!externalConfig || typeof externalConfig !== 'object' || Object.keys(externalConfig).length === 0) { | ||
| return currentConfig; | ||
| } | ||
| config.preloadOpentelemetry = defaults.preloadOpentelemetry; | ||
| } | ||
| Object.keys(externalConfig).forEach(key => { | ||
| const path = `${basePath}.${key}`; | ||
| const currentMeta = configStore.get(path); | ||
| if (currentMeta && currentMeta.source < source) { | ||
| logger.debug(`[config] Skipping ${path}: current source ${currentMeta.source} > incoming ${source}`); | ||
| return; | ||
| } | ||
| const incomingValue = externalConfig[key]; | ||
| if (incomingValue && typeof incomingValue === 'object' && !Array.isArray(incomingValue)) { | ||
| if (!target[key] || typeof target[key] !== 'object') { | ||
| target[key] = {}; | ||
| } | ||
| update({ externalConfig: incomingValue, source, target: target[key], basePath: path }); | ||
| } else { | ||
| target[key] = incomingValue; | ||
| configStore.set(path, { source }); | ||
| util.log({ | ||
| configPath: path, | ||
| source, | ||
| value: incomingValue | ||
| }); | ||
| } | ||
| }); | ||
| return currentConfig; | ||
| }; |
+9
-16
@@ -184,2 +184,11 @@ /* | ||
| /** | ||
| * @param {import('./config').InstanaConfig} config | ||
| */ | ||
| exports.activate = function activate(config) { | ||
| if (config.secrets.matcherMode && config.secrets.keywords) { | ||
| isSecretInternal = matchers[config.secrets.matcherMode](config.secrets.keywords); | ||
| } | ||
| }; | ||
| exports.matchers = matchers; | ||
@@ -191,17 +200,1 @@ | ||
| }; | ||
| /** | ||
| * @param {import('@instana/core/src/config').MatchingOption} matcherId | ||
| * @param {Array.<string>} secretsList | ||
| */ | ||
| exports.setMatcher = function setMatcher(matcherId, secretsList) { | ||
| if (!(typeof matcherId === 'string')) { | ||
| logger.warn(`Received invalid secrets configuration, attribute matcher is not a string: ${matcherId}`); | ||
| } else if (Object.keys(exports.matchers).indexOf(matcherId) < 0) { | ||
| logger.warn(`Received invalid secrets configuration, matcher is not supported: ${matcherId}`); | ||
| } else if (!Array.isArray(secretsList)) { | ||
| logger.warn(`Received invalid secrets configuration, attribute list is not an array: ${secretsList}`); | ||
| } else { | ||
| isSecretInternal = exports.matchers[matcherId](secretsList); | ||
| } | ||
| }; |
@@ -9,2 +9,3 @@ /* | ||
| const sdk = require('./sdk'); | ||
| const secrets = require('../secrets'); | ||
| const constants = require('./constants'); | ||
@@ -264,8 +265,9 @@ const tracingMetrics = require('./metrics'); | ||
| exports.activate = function activate(extraConfig = {}) { | ||
| exports.activate = function activate(_config = config) { | ||
| if (tracingEnabled && !tracingActivated) { | ||
| tracingActivated = true; | ||
| coreUtil.activate(extraConfig); | ||
| tracingUtil.activate(extraConfig); | ||
| spanBuffer.activate(extraConfig); | ||
| coreUtil.activate(_config); | ||
| tracingUtil.activate(_config); | ||
| spanBuffer.activate(_config); | ||
| secrets.activate(_config); | ||
| opentracing.activate(); | ||
@@ -290,3 +292,3 @@ sdk.activate(); | ||
| ) { | ||
| instrumentationModules[instrumentationKey].activate(extraConfig); | ||
| instrumentationModules[instrumentationKey].activate(_config); | ||
| } | ||
@@ -293,0 +295,0 @@ }); |
@@ -16,9 +16,68 @@ /* | ||
| // Maps database connection info to Prisma instances for span creation | ||
| // Key: engine instance (accessible during span creation via ctx._engine) | ||
| // Value: { provider: string, dataSourceUrl: string } | ||
| const providerAndDataSourceUriMap = new WeakMap(); | ||
| // Temporary storage for MariaDB adapter connection URLs | ||
| // Key: adapter instance | ||
| // Value: { provider: string, dataSourceUrl: string } | ||
| // Why two maps? Adapter config is only accessible during construction, but spans only have access to the engine. | ||
| // Flow: 1) Adapter constructor captures URL → mariadbAdapterConfigMap | ||
| // 2) PrismaClient constructor transfers URL → providerAndDataSourceUriMap (keyed by engine) | ||
| // 3) Span creation retrieves URL using engine instance | ||
| const mariadbAdapterConfigMap = new WeakMap(); | ||
| exports.init = function init(config) { | ||
| logger = config.logger; | ||
| hook.onModuleLoad('@prisma/adapter-mariadb', instrumentMariaDbAdapter, { nativeEsm: true }); | ||
| hook.onModuleLoad('@prisma/client', instrumentPrismaClient); | ||
| }; | ||
| function instrumentMariaDbAdapter(mariadbAdapterModule) { | ||
| const OriginalPrismaMariaDb = mariadbAdapterModule?.PrismaMariaDb; | ||
| if (typeof OriginalPrismaMariaDb !== 'function') { | ||
| return mariadbAdapterModule; | ||
| } | ||
| class InstanaPrismaMariaDb extends OriginalPrismaMariaDb { | ||
| constructor(...args) { | ||
| super(...args); | ||
| const [config] = args; | ||
| if (!config || typeof config !== 'object') { | ||
| return; | ||
| } | ||
| const { host = 'localhost', port = 3306, user = '', database = '' } = config; | ||
| if (!user || !database) { | ||
| return; | ||
| } | ||
| const sanitizedUrl = `mysql://${user}:_redacted_@${host}:${port}/${database}`; | ||
| mariadbAdapterConfigMap.set(this, { | ||
| // Prisma MariaDB adapter reports mysql as provider | ||
| provider: 'mysql', | ||
| url: sanitizedUrl | ||
| }); | ||
| } | ||
| } | ||
| const isGetterExport = !!Object.getOwnPropertyDescriptor(mariadbAdapterModule, 'PrismaMariaDb')?.get; | ||
| // CJS: PrismaMariaDb is a getter property | ||
| if (isGetterExport) { | ||
| return { | ||
| ...mariadbAdapterModule, | ||
| get PrismaMariaDb() { | ||
| return InstanaPrismaMariaDb; | ||
| } | ||
| }; | ||
| } | ||
| // ESM: PrismaMariaDb is a direct export | ||
| mariadbAdapterModule.PrismaMariaDb = InstanaPrismaMariaDb; | ||
| return mariadbAdapterModule; | ||
| } | ||
| function instrumentPrismaClient(prismaClientModule) { | ||
@@ -90,3 +149,7 @@ instrumentClientConstructor(prismaClientModule); | ||
| try { | ||
| if (adapter?.config?.connectionString) { | ||
| // Get URL captured during MariaDB adapter construction | ||
| const capturedConfig = mariadbAdapterConfigMap.get(adapter); | ||
| if (capturedConfig) { | ||
| dataSourceUrl = capturedConfig.url; | ||
| } else if (adapter?.config?.connectionString) { | ||
| dataSourceUrl = redactPassword(provider, adapter.config.connectionString); | ||
@@ -93,0 +156,0 @@ } else if (adapter?.externalPool?.options?.connectionString) { |
@@ -31,8 +31,5 @@ /* | ||
| exports.activate = function activate(extraConfig) { | ||
| if (extraConfig && extraConfig.tracing && extraConfig.tracing.kafka) { | ||
| if (extraConfig.tracing.kafka.traceCorrelation != null) { | ||
| traceCorrelationEnabled = extraConfig.tracing.kafka.traceCorrelation; | ||
| } | ||
| } | ||
| // TODO: We will remove _config as soon as the config object is a config instance (`config.get`) | ||
| exports.activate = function activate(_config) { | ||
| traceCorrelationEnabled = _config.tracing.kafka.traceCorrelation; | ||
| isActive = true; | ||
@@ -39,0 +36,0 @@ }; |
@@ -30,9 +30,6 @@ /* | ||
| }; | ||
| // The extraConfig is coming from the agent configs. You can set the kafka format in the agent. | ||
| exports.activate = function activate(extraConfig) { | ||
| if (extraConfig && extraConfig.tracing && extraConfig.tracing.kafka) { | ||
| if (extraConfig.tracing.kafka.traceCorrelation != null) { | ||
| traceCorrelationEnabled = extraConfig.tracing.kafka.traceCorrelation; | ||
| } | ||
| } | ||
| // | ||
| exports.activate = function activate(_config) { | ||
| traceCorrelationEnabled = _config.tracing.kafka.traceCorrelation; | ||
| isActive = true; | ||
@@ -39,0 +36,0 @@ }; |
@@ -38,11 +38,5 @@ /* | ||
| exports.activate = function activate(extraConfig) { | ||
| if ( | ||
| extraConfig && | ||
| extraConfig.tracing && | ||
| extraConfig.tracing.http && | ||
| Array.isArray(extraConfig.tracing.http.extraHttpHeadersToCapture) | ||
| ) { | ||
| extraHttpHeadersToCapture = extraConfig.tracing.http.extraHttpHeadersToCapture; | ||
| } | ||
| exports.activate = function activate(_config) { | ||
| extraHttpHeadersToCapture = _config.tracing.http.extraHttpHeadersToCapture; | ||
| isActive = true; | ||
@@ -49,0 +43,0 @@ }; |
@@ -41,11 +41,5 @@ /* | ||
| exports.activate = function activate(extraConfig) { | ||
| if ( | ||
| extraConfig && | ||
| extraConfig.tracing && | ||
| extraConfig.tracing.http && | ||
| Array.isArray(extraConfig.tracing.http.extraHttpHeadersToCapture) | ||
| ) { | ||
| extraHttpHeadersToCapture = extraConfig.tracing.http.extraHttpHeadersToCapture; | ||
| } | ||
| exports.activate = function activate(_config) { | ||
| extraHttpHeadersToCapture = _config.tracing.http.extraHttpHeadersToCapture; | ||
| isActive = true; | ||
@@ -52,0 +46,0 @@ }; |
@@ -46,11 +46,5 @@ /* | ||
| exports.activate = function activate(extraConfig) { | ||
| if ( | ||
| extraConfig && | ||
| extraConfig.tracing && | ||
| extraConfig.tracing.http && | ||
| Array.isArray(extraConfig.tracing.http.extraHttpHeadersToCapture) | ||
| ) { | ||
| extraHttpHeadersToCapture = extraConfig.tracing.http.extraHttpHeadersToCapture; | ||
| } | ||
| exports.activate = function activate(_config) { | ||
| extraHttpHeadersToCapture = _config.tracing.http.extraHttpHeadersToCapture; | ||
| isActive = true; | ||
@@ -57,0 +51,0 @@ }; |
@@ -35,11 +35,5 @@ /* | ||
| exports.activate = function activate(extraConfig) { | ||
| if ( | ||
| extraConfig && | ||
| extraConfig.tracing && | ||
| extraConfig.tracing.http && | ||
| Array.isArray(extraConfig.tracing.http.extraHttpHeadersToCapture) | ||
| ) { | ||
| extraHttpHeadersToCapture = extraConfig.tracing.http.extraHttpHeadersToCapture; | ||
| } | ||
| exports.activate = function activate(_config) { | ||
| extraHttpHeadersToCapture = _config.tracing.http.extraHttpHeadersToCapture; | ||
| isActive = true; | ||
@@ -46,0 +40,0 @@ }; |
@@ -56,3 +56,3 @@ /* | ||
| exports.activate = function activate(extraConfig) { | ||
| exports.activate = function activate(_config) { | ||
| if (originalFetch == null) { | ||
@@ -63,10 +63,4 @@ // Do nothing in Node.js versions that do not support native fetch. | ||
| if ( | ||
| extraConfig && | ||
| extraConfig.tracing && | ||
| extraConfig.tracing.http && | ||
| Array.isArray(extraConfig.tracing.http.extraHttpHeadersToCapture) | ||
| ) { | ||
| extraHttpHeadersToCapture = extraConfig.tracing.http.extraHttpHeadersToCapture; | ||
| } | ||
| extraHttpHeadersToCapture = _config.tracing.http.extraHttpHeadersToCapture; | ||
| isActive = true; | ||
@@ -73,0 +67,0 @@ }; |
@@ -106,5 +106,5 @@ /* | ||
| /** | ||
| * @param {import('@instana/collector/src/types/collector').AgentConfig} extraConfig | ||
| * @param {import('../config').InstanaConfig} _config | ||
| */ | ||
| exports.activate = function activate(extraConfig) { | ||
| exports.activate = function activate(_config) { | ||
| if (!downstreamConnection) { | ||
@@ -123,7 +123,3 @@ logger.error('No downstreamConnection has been set.'); | ||
| if (extraConfig?.tracing) { | ||
| if (extraConfig.tracing.spanBatchingEnabled) { | ||
| batchingEnabled = true; | ||
| } | ||
| } | ||
| batchingEnabled = _config.tracing.spanBatchingEnabled; | ||
@@ -130,0 +126,0 @@ isActive = true; |
@@ -13,3 +13,3 @@ /* | ||
| const stackTrace = require('../util/stackTrace'); | ||
| const { DEFAULT_STACK_TRACE_LENGTH, DEFAULT_STACK_TRACE_MODE, STACK_TRACE_MODES } = require('../util/constants'); | ||
| const { STACK_TRACE_MODES } = require('../util/constants'); | ||
@@ -38,22 +38,7 @@ /** @type {import('../core').GenericLogger} */ | ||
| /** | ||
| * @param {import('@instana/collector/src/types/collector').AgentConfig} extraConfig | ||
| * @param {import('../config').InstanaConfig} _config | ||
| */ | ||
| exports.activate = function activate(extraConfig) { | ||
| const agentTraceConfig = extraConfig?.tracing; | ||
| // Note: We check whether the already-initialized stackTraceLength equals the default value. | ||
| // If it does, we can safely override it, since the user did not explicitly configure it. | ||
| // Note: If the user configured a value via env or code and also configured a different value in the agent, | ||
| // but the env/code value happens to equal the default, the agent value would overwrite it. | ||
| // This is a rare edge case and acceptable for now. | ||
| if (agentTraceConfig?.stackTrace && stackTraceMode === DEFAULT_STACK_TRACE_MODE) { | ||
| stackTraceMode = agentTraceConfig.stackTrace; | ||
| } | ||
| // stackTraceLength is valid when set to any number, including 0 | ||
| if (agentTraceConfig?.stackTraceLength != null && stackTraceLength === DEFAULT_STACK_TRACE_LENGTH) { | ||
| stackTraceLength = agentTraceConfig.stackTraceLength; | ||
| } | ||
| exports.activate = function activate(_config) { | ||
| stackTraceLength = _config.tracing.stackTraceLength; | ||
| stackTraceMode = _config.tracing.stackTrace; | ||
| }; | ||
@@ -60,0 +45,0 @@ |
@@ -20,1 +20,8 @@ /* | ||
| }; | ||
| exports.CONFIG_SOURCES = { | ||
| ENV: 1, | ||
| INCODE: 2, | ||
| AGENT: 3, | ||
| DEFAULT: 4 | ||
| }; |
@@ -12,5 +12,2 @@ /* | ||
| /** @type {import('@instana/collector/src/types/collector').AgentConfig} */ | ||
| let agentConfig; | ||
| /** | ||
@@ -24,6 +21,6 @@ * @param {import('../config').InstanaConfig} _config | ||
| /** | ||
| * @param {import('@instana/collector/src/types/collector').AgentConfig} _agentConfig | ||
| * @param {import('../config').InstanaConfig} _config | ||
| */ | ||
| function activate(_agentConfig) { | ||
| agentConfig = _agentConfig; | ||
| function activate(_config) { | ||
| config = _config; | ||
| } | ||
@@ -115,3 +112,2 @@ | ||
| // Give priority to service-level config | ||
| if (config && shouldDisable(config, context)) { | ||
@@ -121,8 +117,2 @@ return true; | ||
| // Fallback to agent-level config if not disabled above | ||
| // NOTE: We currently have no single config object. | ||
| if (agentConfig && shouldDisable(agentConfig, context)) { | ||
| return true; | ||
| } | ||
| return false; | ||
@@ -129,0 +119,0 @@ } |
@@ -47,7 +47,7 @@ /* | ||
| /** | ||
| * @param {import('@instana/collector/src/types/collector').AgentConfig} extraConfig | ||
| * @param {import('@instana/core/src/config').InstanaConfig} config | ||
| */ | ||
| exports.activate = function activate(extraConfig) { | ||
| disableInstrumentation.activate(extraConfig); | ||
| spanFilter.activate(extraConfig); | ||
| exports.activate = function activate(config) { | ||
| disableInstrumentation.activate(config); | ||
| spanFilter.activate(config); | ||
| }; | ||
@@ -54,0 +54,0 @@ |
@@ -22,22 +22,7 @@ /* | ||
| /** | ||
| * @param {import('@instana/collector/src/types/collector').AgentConfig} extraConfig | ||
| * @param {import('../config').InstanaConfig} _config | ||
| */ | ||
| function activate(extraConfig) { | ||
| /** | ||
| * Configuration priority order: | ||
| * 1. In-code configuration | ||
| * 2. Environment variables: | ||
| * - `INSTANA_IGNORE_ENDPOINTS_PATH` | ||
| * - `INSTANA_IGNORE_ENDPOINTS` | ||
| * 3. Agent configuration (loaded later) | ||
| * | ||
| * Since the agent configuration is loaded later, we first check | ||
| * that `ignoreEndpoints` MUST be empty. If yes, we | ||
| * are allowed to fall back to the agent's configuration (`extraConfig.tracing.ignoreEndpoints`). | ||
| * | ||
| * TODO: Perform a major refactoring of configuration priority ordering in INSTA-817. | ||
| */ | ||
| const isIgnoreEndpointsEmpty = !ignoreEndpoints || Object.keys(ignoreEndpoints).length === 0; | ||
| if (isIgnoreEndpointsEmpty && extraConfig?.tracing?.ignoreEndpoints) { | ||
| ignoreEndpoints = extraConfig.tracing.ignoreEndpoints; | ||
| function activate(_config) { | ||
| if (_config?.tracing?.ignoreEndpoints) { | ||
| ignoreEndpoints = _config.tracing.ignoreEndpoints; | ||
| } | ||
@@ -44,0 +29,0 @@ } |
Sorry, the diff of this file is too big to display
Network access
Supply chain riskThis module accesses the network.
Found 4 instances in 1 package
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 2 instances in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 18 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 2 instances in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
Network access
Supply chain riskThis module accesses the network.
Found 4 instances in 1 package
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 2 instances in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 30 instances in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
AI-detected potential code anomaly
Supply chain riskAI has identified unusual behaviors that may pose a security risk.
Found 1 instance in 1 package
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Found 1 instance in 1 package
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Found 1 instance in 1 package
930423
2.27%155
1.31%22859
1.76%100
-9.09%