Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
31 commits
Select commit Hold shift + click to select a range
0195122
refactor(core): normalized numeric config resolution with env precede…
aryamohanan Mar 26, 2026
b741c56
fix: removed deprecated INSTANA_DEV_MIN_DELAY_BEFORE_SENDING_SPANS en…
aryamohanan Mar 27, 2026
7b4afb0
refactor(core): normalized boolean config resolution (#2433)
aryamohanan Mar 27, 2026
2aca39f
refactor(core): enabled eslint in config (#2435)
aryamohanan Mar 27, 2026
9d19b9d
refactor(core): added TODO comments explaining resolver complexity(#2…
aryamohanan Mar 30, 2026
9a8ae88
test: restructured config tests with logical grouping (#2445)
aryamohanan Mar 30, 2026
3f57cc4
test: extended tests to include precedence cases (#2447)
aryamohanan Mar 31, 2026
4f6eb2e
test(config): improved overall test coverage (#2451)
aryamohanan Mar 31, 2026
42c7a58
test(collector): extended config tests for full coverage (#2456)
aryamohanan Apr 1, 2026
1e7a48b
refactor: standardized config logging format (#2446)
aryamohanan Apr 6, 2026
0253ef0
refactor(core): made config resolution explicit (#2448)
aryamohanan Apr 8, 2026
e8be4e1
refactor(core): adapted optional object parameters in config normaliz…
abhilash-sivan Apr 8, 2026
1e7fb05
refactor(collector): refcatored config module (#2457)
aryamohanan Apr 8, 2026
2d414f2
refactor(config): cleanup normalize http header config (#2477)
aryamohanan Apr 8, 2026
4df1e37
refactor(collector): moved resolve string config to core utility (#2479)
aryamohanan Apr 10, 2026
e1ba962
fix(core): ensured env vars take precedence over in-code config (#2478)
aryamohanan Apr 10, 2026
4db9538
test(collector): enabled config precedence tests (#2480)
aryamohanan Apr 10, 2026
4e8cfad
test: only set INSTANA_TRACING_DISABLE when tracing is actually disabled
aryamohanan Apr 13, 2026
9b0ffcf
chore: only parse INSTANA_TRACING_DISABLE as boolean when value is bo…
aryamohanan Apr 13, 2026
aa3546a
fix: corrected the config precedence for agent config (#2492)
aryamohanan Apr 20, 2026
bb21e41
test(config): added test coverage for config resolver (#2503)
aryamohanan Apr 20, 2026
32822f0
refactor(config): align disable normalizer with resolver return shape…
aryamohanan Apr 20, 2026
93098a7
chore(config): preserved existing values when applying partial update…
aryamohanan Apr 21, 2026
0b9b1a1
refactor(config): replaced agentConfig usage with updated config (#2507)
aryamohanan Apr 21, 2026
fe060f9
chore: fixed failing config related tests (#2508)
aryamohanan Apr 22, 2026
9150226
refactor(core): replaced agent config with updated config in instrume…
aryamohanan Apr 23, 2026
e3000a6
test: added config precedence integretion tests (#2515)
aryamohanan Apr 29, 2026
6ef803e
chore: added logging (#2514)
aryamohanan May 7, 2026
30899df
chore: incorporated latest config updates (#2537)
aryamohanan May 8, 2026
d4fc4a2
chore: corrected setting config precedence for secret config (#2546)
aryamohanan May 13, 2026
6e19d76
chore: respected config precedence for agent-provided secrets (#2547)
aryamohanan May 13, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions packages/aws-lambda/src/wrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ const latestRuntime = semver.gte(process.version, '24.0.0');

const logger = serverlessLogger.init();
coreConfig.init(logger);
let config = coreConfig.normalize({}, lambdaConfigDefaults);
let config = coreConfig.normalize({ defaultsOverride: lambdaConfigDefaults });
let coldStart = true;

// Initialize instrumentations early to allow for require statements after our
Expand Down Expand Up @@ -286,7 +286,7 @@ function init(event, arnInfo, _config) {
// - late env variables (less likely)
// - custom logger
// - we always renormalize unconditionally to ensure safety.
config = coreConfig.normalize(userConfig, lambdaConfigDefaults);
config = coreConfig.normalize({ userConfig, defaultsOverride: lambdaConfigDefaults });

if (!config.tracing.enabled) {
return false;
Expand Down
8 changes: 6 additions & 2 deletions packages/collector/src/announceCycle/agentready.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ try {
// Worker threads are not available, so we know that this is the main thread.
}

const { tracing } = require('@instana/core');
const { tracing, coreConfig, util } = require('@instana/core');
const agentConnection = require('../agentConnection');
const agentOpts = require('../agent/opts');
const initializedTooLate = require('../util/initializedTooLate');
Expand Down Expand Up @@ -130,7 +130,11 @@ function enter(_ctx) {
}
}

tracing.activate(agentOpts.config);
const updatedConfig = coreConfig.update({
externalConfig: agentOpts.config,
source: util.constants.CONFIG_SOURCES.AGENT
});
tracing.activate(updatedConfig);

if (agentOpts.autoProfile && autoprofile) {
profiler = autoprofile.start();
Expand Down
6 changes: 4 additions & 2 deletions packages/collector/src/announceCycle/unannounced.js
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,9 @@ function applySecretsConfiguration(agentResponse) {
${agentResponse.secrets.list}`
);
} else {
secrets.setMatcher(agentResponse.secrets.matcher, agentResponse.secrets.list);
ensureNestedObjectExists(agentOpts.config, ['secrets']);
agentOpts.config.secrets.matcherMode = agentResponse.secrets.matcher;
agentOpts.config.secrets.keywords = agentResponse.secrets.list;
}
}
}
Expand Down Expand Up @@ -305,7 +307,7 @@ function applyDisableConfiguration(agentResponse) {
ensureNestedObjectExists(agentOpts.config, ['tracing', 'disable']);
agentOpts.config.tracing.disable = configNormalizers.disable.normalizeExternalConfig({
tracing: { disable: disablingConfig }
});
}).value;
}
module.exports = {
init,
Expand Down
9 changes: 6 additions & 3 deletions packages/collector/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,7 @@ const instanaSharedMetrics = require('@instana/shared-metrics');
require('./tracing'); // load additional instrumentations

const log = require('./logger');
const normalizeCollectorConfig = require('./util/normalizeConfig');
const normalizeConfig = require('./util/normalizeConfig');
const experimental = require('./experimental');

// NOTE: Default collector logger && config for cases like `preinit`.
Expand Down Expand Up @@ -158,8 +158,11 @@ function init(userConfig = {}) {
log.init(userConfig);
}

config = normalizeCollectorConfig(userConfig);
config = instanaNodeJsCore.coreConfig.normalize(config);
const finalCollectorConfig = normalizeConfig(userConfig);
config = instanaNodeJsCore.coreConfig.normalize({
userConfig,
finalConfigBase: finalCollectorConfig
});

agentConnection = require('./agentConnection');
const agentOpts = require('./agent/opts');
Expand Down
2 changes: 1 addition & 1 deletion packages/collector/src/types/collector.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ export interface CollectorConfig {
stackTraceLength?: number;
[key: string]: any;
};
autoProfile?: boolean | string;
autoProfile?: boolean;
reportUnhandledPromiseRejections?: boolean;
logger?: GenericLogger;
level?: string | number;
Expand Down
142 changes: 111 additions & 31 deletions packages/collector/src/util/normalizeConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@
* (c) Copyright Instana Inc. and contributors 2019
*/

/* eslint-disable dot-notation */

'use strict';

const util = require('@instana/core/src/config/util');
const validate = require('@instana/core/src/config/validator');

const defaults = {
agentHost: '127.0.0.1',
agentPort: 42699,
Expand All @@ -17,43 +18,122 @@ const defaults = {

/**
* Merges the config that was passed to the init function with environment variables and default values.
* @param {import('../types/collector').CollectorConfig} config
* @param {import('../types/collector').CollectorConfig} userConfig
* @returns {import('../types/collector').CollectorConfig}
*/
module.exports = function normalizeConfig(config = {}) {
config.agentHost = config.agentHost || process.env.INSTANA_AGENT_HOST || defaults.agentHost;
config.agentPort = config.agentPort || parseToPositiveInteger(process.env.INSTANA_AGENT_PORT, defaults.agentPort);
config.agentRequestTimeout =
config.agentRequestTimeout ||
parseToPositiveInteger(process.env.INSTANA_AGENT_REQUEST_TIMEOUT, defaults.agentRequestTimeout);
module.exports = function normalizeConfig(userConfig = {}) {
const finalConfig = {};

config.autoProfile = config.autoProfile || process.env.INSTANA_AUTO_PROFILE || defaults.autoProfile;
config.tracing = config.tracing || {};
// NOTE: This function only normalizes collector-specific configuration fields.
// Other userConfig fields (like serviceName, tracing, etc.) are passed through as-is
// and will be normalized later by core/config when this collector config is passed
// as extraFinalConfig to core's normalize function.
finalConfig.agentHost = normalizeAgentHost(userConfig, defaults);
finalConfig.agentPort = normalizeAgentPort(userConfig, defaults);
finalConfig.agentRequestTimeout = normalizeAgentRequestTimeout(userConfig, defaults);
finalConfig.autoProfile = normalizeAutoProfile(userConfig, defaults);
finalConfig.reportUnhandledPromiseRejections = normalizeUnhandledRejections(userConfig);
finalConfig.disableCollectorInitEvent = normalizeCollectorInitEvent(userConfig);
finalConfig.tracing = userConfig.tracing || {};

if (config.reportUnhandledPromiseRejections == null) {
config.reportUnhandledPromiseRejections = false;
}
return finalConfig;
};

if (config.disableCollectorInitEvent == null) {
const envValue = process.env.INSTANA_DISABLE_COLLECTOR_INIT_EVENT;
config.disableCollectorInitEvent = envValue === 'true' ? true : defaults.disableCollectorInitEvent;
}
/**
* @param {import('../types/collector').CollectorConfig} userConfig
* @param {{ agentHost: string }} defaultConfig
* @returns {string}
*/
function normalizeAgentHost(userConfig, defaultConfig) {
const { value } = util.resolve(
{
envValue: 'INSTANA_AGENT_HOST',
inCodeValue: userConfig.agentHost,
defaultValue: defaultConfig.agentHost
},
[validate.stringValidator]
);
return value;
}

return config;
};
/**
* @param {import('../types/collector').CollectorConfig} userConfig
* @param {{ agentPort: number }} defaultConfig
* @returns {number}
*/
function normalizeAgentPort(userConfig, defaultConfig) {
const { value } = util.resolve(
{
envValue: 'INSTANA_AGENT_PORT',
inCodeValue: userConfig.agentPort,
defaultValue: defaultConfig.agentPort
},
[validate.numberValidator]
);
return value;
}

/**
* @param {string | number} value
* @param {number} defaultValue
* @param {import('../types/collector').CollectorConfig} userConfig
* @param {{ agentRequestTimeout: number }} defaultConfig
* @returns {number}
*/
function parseToPositiveInteger(value, defaultValue) {
if (typeof value !== 'string') {
return defaultValue;
}
value = parseInt(value, 10);
if (!isNaN(value)) {
return Math.abs(Math.round(value));
}
return defaultValue;
function normalizeAgentRequestTimeout(userConfig, defaultConfig) {
const { value } = util.resolve(
{
envValue: 'INSTANA_AGENT_REQUEST_TIMEOUT',
inCodeValue: userConfig.agentRequestTimeout,
defaultValue: defaultConfig.agentRequestTimeout
},
[validate.numberValidator]
);
return value;
}

/**
* @param {import('../types/collector').CollectorConfig} userConfig
* @param {{ autoProfile: boolean }} defaultConfig
* @returns {boolean}
*/
function normalizeAutoProfile(userConfig, defaultConfig) {
const { value } = util.resolve(
{
envValue: 'INSTANA_AUTO_PROFILE',
inCodeValue: userConfig.autoProfile,
defaultValue: defaultConfig.autoProfile
},
[validate.booleanValidator]
);
return value;
}

/**
* @param {import('../types/collector').CollectorConfig} userConfig
* @returns {boolean}
*/
function normalizeUnhandledRejections(userConfig) {
const { value } = util.resolve(
{
inCodeValue: userConfig.reportUnhandledPromiseRejections,
defaultValue: false
},
[validate.booleanValidator]
);
return value;
}

/**
* @param {import('../types/collector').CollectorConfig} userConfig
* @returns {boolean}
*/
function normalizeCollectorInitEvent(userConfig) {
const { value } = util.resolve(
{
envValue: 'INSTANA_DISABLE_COLLECTOR_INIT_EVENT',
inCodeValue: userConfig.disableCollectorInitEvent,
defaultValue: defaults.disableCollectorInitEvent
},
[validate.booleanValidator]
);
return value;
}
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ const {
} = require('@_local/core/test/test_util');
const ProcessControls = require('@_local/collector/test/test_util/ProcessControls');
const globalAgent = require('@_local/collector/test/globalAgent');
const { AgentStubControls } = require('@_local/collector/test/apps/agentStubControls');

// v3 is considered the legacy version.
// - It does not support Redis clustering.
Expand Down Expand Up @@ -938,9 +939,69 @@ module.exports = function (name, version, isLatest, mode) {
}
});

describe('Config precedence', () => {
describe('when both agent config and env var are set, env var takes precedence', () => {
const customAgentControls = new AgentStubControls();
let controls;

before(async () => {
await customAgentControls.startAgent({
ignoreEndpoints: { redis: ['get', 'set'] }
});

controls = new ProcessControls({
agentControls: customAgentControls,
dirname: __dirname,
appName: isLegacyVersion ? 'legacyApp' : 'app',
env: {
LIBRARY_LATEST: isLatest,
LIBRARY_VERSION: version,
LIBRARY_NAME: name,
REDIS_SETUP_TYPE: mode,
INSTANA_IGNORE_ENDPOINTS: 'redis:get;'
}
});
await controls.startAndWaitForAgentConnection(5000, Date.now() + 1000 * 60 * 5);
});

beforeEach(async () => {
await customAgentControls.clearReceivedTraceData();
});

after(async () => {
await customAgentControls.stopAgent();
await controls.stop();
});

it('should use env var config and ignore only get (not set)', async () => {
await controls
.sendRequest({
method: 'POST',
path: '/values',
qs: {
key: 'discount',
value: 50
}
})
.then(async () => {
return retry(async () => {
const spans = await customAgentControls.getSpans();
// 1 x http entry span
// 1 x http client span
// 1 x redis set span (set is NOT ignored because env var only ignores 'get')
expect(spans.length).to.equal(3);

const redisSpans = spans.filter(span => span.n === 'redis');
expect(redisSpans.length).to.equal(1);
expect(redisSpans[0].data.redis.command).to.equal('set');
});
});
});
});
});

mochaSuiteFn('ignore-endpoints:', function () {
describe('when ignore-endpoints is enabled via agent configuration', () => {
const { AgentStubControls } = require('@_local/collector/test/apps/agentStubControls');
const customAgentControls = new AgentStubControls();
let controls;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,57 @@ module.exports = function (name, version, isLatest) {
});
});
});

describe('precedence: when both agent and env are configured, env takes precedence', () => {
let customAgentControls;
let precedenceControls;

before(async () => {
customAgentControls = new AgentStubControls();
await customAgentControls.startAgent({
disable: { console: false }
});

precedenceControls = new ProcessControls({
agentControls: customAgentControls,
dirname: __dirname,
env: {
INSTANA_TRACING_DISABLE_INSTRUMENTATIONS: ['console'],
LIBRARY_LATEST: isLatest,
LIBRARY_VERSION: version,
LIBRARY_NAME: name
}
});
await precedenceControls.startAndWaitForAgentConnection();
});

after(async () => {
await precedenceControls.stop();
await customAgentControls.stopAgent();
});

it('should not trace console.warn calls (env var takes precedence over agent config)', async () => {
await precedenceControls.sendRequest({ path: '/warn' });

await testUtils.retry(async () => {
const spans = await customAgentControls.getSpans();
const httpEntrySpan = verifyHttpRootEntry({
spans,
apiPath: '/warn',
pid: String(precedenceControls.getPid())
});

verifyHttpExit({
spans,
parent: httpEntrySpan,
pid: String(precedenceControls.getPid())
});

const consoleLogSpans = testUtils.getSpansByName(spans, 'log.console');
expect(consoleLogSpans).to.be.empty;
});
});
});
});
});

Expand Down
Loading