Skip to content

Commit 50120cd

Browse files
authored
feat(opentelemetry-node): include OTEL_ and ELASTIC_OTEL_ envvars in preamble log.info (#1151)
This includes all likely to be relevant envvars in the preamble log message to help with clarity and debugging. The *value* of envvars are included if they are on the allowlist of known to not be sensitive envvars. Otherwise the value is redacted. Closes: #1018
1 parent d5fe64c commit 50120cd

File tree

5 files changed

+230
-20
lines changed

5 files changed

+230
-20
lines changed

docs/release-notes/index.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,8 @@ To check for security updates, go to [Security announcements for the Elastic sta
3535

3636
* Add support for TLS/mTLS configuration of the OpAMP client (used for [central configuration](elastic-otel-node://reference/edot-node/configuration.md#central-configuration)). Three new environment variables can be used: `ELASTIC_OTEL_OPAMP_CERTIFICATE`, `ELASTIC_OTEL_OPAMP_CLIENT_CERTIFICATE`, and `ELASTIC_OTEL_OPAMP_CLIENT_KEY`. See [the Configure central configuration doc section](elastic-otel-node://reference/edot-node/configuration.md#configure-central-configuration) for details. [#1044](https://github.com/elastic/elastic-otel-node/issues/1044)
3737

38+
* Improve the "preamble" log message at startup to include some details on envvars set that are relevant to the EDOT Node.js config. This uses an allowlist of non-sensitive envvars to avoid logging sensitive details. [#1018](https://github.com/elastic/elastic-otel-node/issues/1018)
39+
3840
### Fixes [edot-node-next-fixes]
3941

4042

packages/opentelemetry-node/lib/environment.js

Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,144 @@ function restoreEnvironment() {
3636
});
3737
}
3838

39+
/**
40+
* Return an object with all `OTEL_` and `ELASTIC_OTEL_` envvars that are **safe
41+
* to be logged**. I.e. this redacts the value of any envvar not on the
42+
* allowlist of non-sensitive envvars.
43+
*
44+
* Compare to the equiv in EDOT Python:
45+
* https://github.com/elastic/elastic-otel-python/blob/v1.9.0/src/elasticotel/distro/config.py#L95-L104
46+
*/
47+
function getSafeEdotEnv() {
48+
// While more of a PITA to maintain, I'm opting for an allowlist of known
49+
// envvars with presumed non-sensitive values, to bias on the side of safer.
50+
const edotEnv = {};
51+
for (const k of Object.keys(process.env)) {
52+
if (k.startsWith('OTEL_') || k.startsWith('ELASTIC_OTEL_')) {
53+
if (NON_SENSTITIVE_EDOT_ENV_NAMES.has(k)) {
54+
edotEnv[k] = process.env[k];
55+
} else {
56+
edotEnv[k] = '[REDACTED]';
57+
}
58+
}
59+
}
60+
return edotEnv;
61+
}
62+
63+
/**
64+
* A set of EDOT/OTel-related envvar names whose value is not considered
65+
* sensitive information. For example the `*_HEADERS` envvars should NOT be
66+
* included in this set.
67+
*
68+
* Command to grep a repo for candidates:
69+
* rg '\b((ELASTIC_)?OTEL_\w+)\b' -g '!test' -oIN | sort | uniq | rg -v HEADERS
70+
* Or in the relevant repo clones:
71+
-g '!test' -g '!semantic-conventions' -g '!CHANGELOG.md' -g '!contrib-test-utils' \
72+
73+
rg '\b((ELASTIC_)?OTEL_\w+)\b' -oIN \
74+
-g '*.{ts,js,mjs,cjs}' -g '!test' -g '!semantic-conventions' -g '!contrib-test-utils' \
75+
elastic-otel-node opentelemetry-js opentelemetry-js-contrib \
76+
| rg -v '(HEADERS|PASSWORD)' \
77+
| rg -v '(_SYMBOL|OTEL_SEV_NUM_FROM_|OTEL_FOO|OTEL_PATCHED_SYMBOL|OTEL_OPEN_SPANS|_$)' \
78+
| sort | uniq
79+
*/
80+
const NON_SENSTITIVE_EDOT_ENV_NAMES = new Set(
81+
`
82+
ELASTIC_OTEL_CONTEXT_PROPAGATION_ONLY
83+
ELASTIC_OTEL_EXPERIMENTAL_OPAMP_HEARTBEAT_INTERVAL
84+
ELASTIC_OTEL_HOST_METRICS_DISABLED
85+
ELASTIC_OTEL_METRICS_DISABLED
86+
ELASTIC_OTEL_NODE_ENABLE_LOG_SENDING
87+
ELASTIC_OTEL_OPAMP_CERTIFICATE
88+
ELASTIC_OTEL_OPAMP_CLIENT_CERTIFICATE
89+
ELASTIC_OTEL_OPAMP_CLIENT_KEY
90+
ELASTIC_OTEL_OPAMP_ENDPOINT
91+
ELASTIC_OTEL_TEST_OPAMP_CLIENT_DIAG_ENABLED
92+
OTEL_ATTRIBUTE_COUNT_LIMIT
93+
OTEL_ATTRIBUTE_VALUE_LENGTH_LIMIT
94+
OTEL_BLRP_EXPORT_TIMEOUT
95+
OTEL_BLRP_MAX_EXPORT_BATCH_SIZE
96+
OTEL_BLRP_MAX_QUEUE_SIZE
97+
OTEL_BLRP_SCHEDULE_DELAY
98+
OTEL_BSP_EXPORT_TIMEOUT
99+
OTEL_BSP_MAX_EXPORT_BATCH_SIZE
100+
OTEL_BSP_MAX_QUEUE_SIZE
101+
OTEL_BSP_SCHEDULE_DELAY
102+
OTEL_EVENT_ATTRIBUTE_COUNT_LIMIT
103+
OTEL_EXPERIMENTAL_CONFIG_FILE
104+
OTEL_EXPORTER_JAEGER_AGENT_HOST
105+
OTEL_EXPORTER_JAEGER_AGENT_PORT
106+
OTEL_EXPORTER_JAEGER_ENDPOINT
107+
OTEL_EXPORTER_JAEGER_USER
108+
OTEL_EXPORTER_OTLP_CERTIFICATE
109+
OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE
110+
OTEL_EXPORTER_OTLP_CLIENT_KEY
111+
OTEL_EXPORTER_OTLP_COMPRESSION
112+
OTEL_EXPORTER_OTLP_ENDPOINT
113+
OTEL_EXPORTER_OTLP_INSECURE
114+
OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE
115+
OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE
116+
OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY
117+
OTEL_EXPORTER_OTLP_LOGS_COMPRESSION
118+
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT
119+
OTEL_EXPORTER_OTLP_LOGS_PROTOCOL
120+
OTEL_EXPORTER_OTLP_LOGS_TIMEOUT
121+
OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE
122+
OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE
123+
OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY
124+
OTEL_EXPORTER_OTLP_METRICS_COMPRESSION
125+
OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION
126+
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT
127+
OTEL_EXPORTER_OTLP_METRICS_PROTOCOL
128+
OTEL_EXPORTER_OTLP_METRICS_TEMPORALITY_PREFERENCE
129+
OTEL_EXPORTER_OTLP_METRICS_TIMEOUT
130+
OTEL_EXPORTER_OTLP_PROTOCOL
131+
OTEL_EXPORTER_OTLP_TIMEOUT
132+
OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE
133+
OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE
134+
OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY
135+
OTEL_EXPORTER_OTLP_TRACES_COMPRESSION
136+
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT
137+
OTEL_EXPORTER_OTLP_TRACES_PROTOCOL
138+
OTEL_EXPORTER_OTLP_TRACES_TIMEOUT
139+
OTEL_EXPORTER_PROMETHEUS_HOST
140+
OTEL_EXPORTER_PROMETHEUS_PORT
141+
OTEL_EXPORTER_ZIPKIN_ENDPOINT
142+
OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT
143+
OTEL_INSTRUMENTATION_HTTP_KNOWN_METHODS
144+
OTEL_LINK_ATTRIBUTE_COUNT_LIMIT
145+
OTEL_LOG_LEVEL
146+
OTEL_LOGRECORD_ATTRIBUTE_COUNT_LIMIT
147+
OTEL_LOGRECORD_ATTRIBUTE_VALUE_LENGTH_LIMIT
148+
OTEL_LOGS_EXPORTER
149+
OTEL_METRIC_EXPORT_INTERVAL
150+
OTEL_METRIC_EXPORT_TIMEOUT
151+
OTEL_METRICS_EXEMPLAR_FILTER
152+
OTEL_METRICS_EXPORTER
153+
OTEL_NODE_DISABLED_INSTRUMENTATIONS
154+
OTEL_NODE_ENABLED_INSTRUMENTATIONS
155+
OTEL_NODE_RESOURCE_DETECTORS
156+
OTEL_PROPAGATORS
157+
OTEL_RESOURCE_ATTRIBUTES
158+
OTEL_SDK_DISABLED
159+
OTEL_SEMCONV_STABILITY_OPT_IN
160+
OTEL_SERVICE_NAME
161+
OTEL_SPAN_ATTRIBUTE_COUNT_LIMIT
162+
OTEL_SPAN_ATTRIBUTE_PER_EVENT_COUNT_LIMIT
163+
OTEL_SPAN_ATTRIBUTE_PER_LINK_COUNT_LIMIT
164+
OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT
165+
OTEL_SPAN_EVENT_COUNT_LIMIT
166+
OTEL_SPAN_LINK_COUNT_LIMIT
167+
OTEL_TRACES_EXPORTER
168+
OTEL_TRACES_SAMPLER
169+
OTEL_TRACES_SAMPLER_ARG
170+
`
171+
.trim()
172+
.split(/\s+/)
173+
);
174+
39175
module.exports = {
40176
setupEnvironment,
41177
restoreEnvironment,
178+
getSafeEdotEnv,
42179
};

packages/opentelemetry-node/lib/sdk.js

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,11 @@ const {createAddHookMessageChannel} = require('import-in-the-middle');
3636
const {log, registerOTelDiagLogger} = require('./logging');
3737
const luggite = require('./luggite');
3838
const {resolveDetectors} = require('./detectors');
39-
const {setupEnvironment, restoreEnvironment} = require('./environment');
39+
const {
40+
setupEnvironment,
41+
restoreEnvironment,
42+
getSafeEdotEnv,
43+
} = require('./environment');
4044
const {getInstrumentations} = require('./instrumentations');
4145
const {setupCentralConfig} = require('./central-config');
4246
const {
@@ -97,6 +101,10 @@ function setupShutdownHandlers(shutdownFn) {
97101
function startNodeSDK(cfg = {}) {
98102
log.trace('startNodeSDK cfg:', cfg);
99103

104+
// Gather relevant envvars for the preamble, before this distro possibly
105+
// mucks with some.
106+
const edotEnv = getSafeEdotEnv();
107+
100108
// TODO: test behaviour with OTEL_SDK_DISABLED.
101109
// Do we still log preamble? See NodeSDK _disabled handling.
102110
// Do we still attempt to enableHostMetrics()?
@@ -277,25 +285,6 @@ function startNodeSDK(cfg = {}) {
277285

278286
setupEnvironment();
279287
const sdk = new NodeSDK(config);
280-
281-
// TODO perhaps include some choice resource attrs in this log (sync ones): service.name, deployment.environment.name
282-
log.info(
283-
{
284-
preamble: true,
285-
distroVersion: DISTRO_VERSION,
286-
env: {
287-
// For darwin: https://en.wikipedia.org/wiki/Darwin_%28operating_system%29#Release_history
288-
os: `${os.platform()} ${os.release()}`,
289-
arch: os.arch(),
290-
runtime: `Node.js ${process.version}`,
291-
},
292-
// The "config" object structure is not stable.
293-
config: {
294-
logLevel: luggite.nameFromLevel[log.level()] ?? log.level(),
295-
},
296-
},
297-
'start EDOT Node.js'
298-
);
299288
sdk.start(); // .start() *does* use `process.env` though I think it should not.
300289
restoreEnvironment();
301290

@@ -338,6 +327,28 @@ function startNodeSDK(cfg = {}) {
338327
contextPropagationOnly,
339328
});
340329

330+
// Log a preamble by default. The primary goal is for getting clarity on
331+
// what is running and configure how for *support*.
332+
// TODO perhaps include some choice resource attrs in this log (sync ones): service.name, deployment.environment.name
333+
log.info(
334+
{
335+
preamble: true,
336+
distroVersion: DISTRO_VERSION,
337+
system: {
338+
// For darwin: https://en.wikipedia.org/wiki/Darwin_%28operating_system%29#Release_history
339+
os: `${os.platform()} ${os.release()}`,
340+
arch: os.arch(),
341+
runtime: `Node.js ${process.version}`,
342+
},
343+
edotEnv,
344+
// The "configInfo" object structure is not stable.
345+
configInfo: {
346+
logLevel: luggite.nameFromLevel[log.level()] ?? log.level(),
347+
},
348+
},
349+
'start EDOT Node.js'
350+
);
351+
341352
// Shutdown handling.
342353
const shutdownFn = () => {
343354
const promises = [sdk.shutdown()];
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/*
2+
* Copyright Elasticsearch B.V. and contributors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
// EDOT Node.js logs a preamble message at startup. This test file ensures
7+
// that preamble includes the intended data.
8+
9+
const {test} = require('tape');
10+
const {runTestFixtures, findObjInArray} = require('./testutils');
11+
12+
/** @type {import('./testutils').TestFixture[]} */
13+
const testFixtures = [
14+
{
15+
name: 'premable basic',
16+
args: ['./fixtures/use-express.js'],
17+
cwd: __dirname,
18+
env: {
19+
NODE_OPTIONS: '--require=@elastic/opentelemetry-node',
20+
OTEL_METRICS_EXPORTER: 'none',
21+
OTEL_EXPORTER_OTLP_HEADERS: 'Foo=bar',
22+
ELASTIC_OTEL_NODE_ENABLE_LOG_SENDING: 'true',
23+
},
24+
verbose: true,
25+
checkTelemetry: (t, col, stdout) => {
26+
const logs = stdout
27+
.split(/\r?\n/g)
28+
.filter((ln) => ln.startsWith('{'))
29+
.map((ln) => JSON.parse(ln));
30+
const preamble = findObjInArray(logs, 'preamble', true);
31+
t.ok(preamble);
32+
t.ok(preamble.system.os);
33+
t.ok(preamble.system.runtime);
34+
t.strictEqual(preamble.level, 30, 'premable logged at INFO level');
35+
t.strictEqual(preamble.edotEnv.OTEL_METRICS_EXPORTER, 'none');
36+
t.strictEqual(
37+
preamble.edotEnv.ELASTIC_OTEL_NODE_ENABLE_LOG_SENDING,
38+
'true'
39+
);
40+
t.strictEqual(
41+
preamble.edotEnv.OTEL_EXPORTER_OTLP_HEADERS,
42+
'[REDACTED]'
43+
);
44+
},
45+
},
46+
];
47+
48+
test('preamble log.info', (suite) => {
49+
runTestFixtures(suite, testFixtures);
50+
suite.end();
51+
});

packages/opentelemetry-node/types/environment.d.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,12 @@ export function setupEnvironment(): void;
66
* Restores any `process.env` stashed in `setupEnvironment()`.
77
*/
88
export function restoreEnvironment(): void;
9+
/**
10+
* Return an object with all `OTEL_` and `ELASTIC_OTEL_` envvars that are **safe
11+
* to be logged**. I.e. this redacts the value of any envvar not on the
12+
* allowlist of non-sensitive envvars.
13+
*
14+
* Compare to the equiv in EDOT Python:
15+
* https://github.com/elastic/elastic-otel-python/blob/v1.9.0/src/elasticotel/distro/config.py#L95-L104
16+
*/
17+
export function getSafeEdotEnv(): {};

0 commit comments

Comments
 (0)