Skip to content

Commit 41a96cb

Browse files
david-lunatrentm
andauthored
feat(otel-node)!: update to SDK 2.0 (#663)
Co-authored-by: Trent Mick <[email protected]>
1 parent 4e2e5b8 commit 41a96cb

File tree

11 files changed

+1225
-1548
lines changed

11 files changed

+1225
-1548
lines changed

packages/opentelemetry-node/CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
# @elastic/opentelemetry-node Changelog
22

3+
## Unreleased
4+
5+
- Upgrade upstream OTel dependencies to SDK 2.0. This should be non-breaking
6+
for users of `node --import @elastic/opentelemetry-node my-app.js` to start
7+
EDOT Node.js for their application.
8+
(https://github.com/elastic/elastic-otel-node/pull/663)
9+
310
## v0.7.0
411

512
- BREAKING CHANGE: Bump min-supported node to `^18.19.0 || >=20.6.0`.

packages/opentelemetry-node/lib/detectors.js

Lines changed: 29 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,10 @@
44
*/
55

66
/**
7-
* NOTE: when `Detector` is finally removed import only `DetectorSync` and
8-
* get rid of the aliasing
9-
* @typedef {import('@opentelemetry/resources').Detector} DetectorOrig
10-
* @typedef {import('@opentelemetry/resources').DetectorSync} DetectorSyncOrig
11-
* @typedef {DetectorOrig | DetectorSyncOrig} DetectorSync
7+
* @typedef {import('@opentelemetry/resources').ResourceDetector} ResourceDetector
128
*/
139

10+
const {getStringListFromEnv} = require('@opentelemetry/core');
1411
const {
1512
alibabaCloudEcsDetector,
1613
} = require('@opentelemetry/resource-detector-alibaba-cloud');
@@ -31,41 +28,41 @@ const {
3128
} = require('@opentelemetry/resource-detector-container');
3229
const {gcpDetector} = require('@opentelemetry/resource-detector-gcp');
3330
const {
34-
envDetectorSync,
35-
hostDetectorSync,
36-
osDetectorSync,
37-
processDetectorSync,
38-
serviceInstanceIdDetectorSync,
39-
Resource,
31+
envDetector,
32+
hostDetector,
33+
osDetector,
34+
processDetector,
35+
serviceInstanceIdDetector,
4036
} = require('@opentelemetry/resources');
4137

42-
const {getEnvVar} = require('./environment');
4338
const {log} = require('./logging');
4439

4540
// @ts-ignore - compiler options do not allow lookp outside `lib` folder
4641
const ELASTIC_SDK_VERSION = require('../package.json').version;
4742

4843
// Elastic's own detector to add distro related metadata
49-
/** @type {DetectorSync} */
50-
const distroDetectorSync = {
44+
/** @type {ResourceDetector} */
45+
const distroDetector = {
5146
detect() {
5247
// TODO: change to semconv resource attribs when
5348
// `@opentelemetry/semantic-conventions` gets updated with the attribs used
5449
// https://github.com/open-telemetry/opentelemetry-js/issues/4235
55-
return new Resource({
56-
'telemetry.distro.name': 'elastic',
57-
'telemetry.distro.version': ELASTIC_SDK_VERSION,
58-
});
50+
return {
51+
attributes: {
52+
'telemetry.distro.name': 'elastic',
53+
'telemetry.distro.version': ELASTIC_SDK_VERSION,
54+
},
55+
};
5956
},
6057
};
6158

62-
/** @type {Record<string, DetectorSync | Array<DetectorSync>>} */
59+
/** @type {Record<string, ResourceDetector | Array<ResourceDetector>>} */
6360
const defaultDetectors = {
64-
env: envDetectorSync,
65-
process: processDetectorSync,
66-
serviceinstance: serviceInstanceIdDetectorSync,
67-
os: osDetectorSync,
68-
host: hostDetectorSync,
61+
env: envDetector,
62+
process: processDetector,
63+
serviceinstance: serviceInstanceIdDetector,
64+
os: osDetector,
65+
host: hostDetector,
6966
container: containerDetector,
7067
alibaba: alibabaCloudEcsDetector,
7168
aws: [
@@ -80,24 +77,26 @@ const defaultDetectors = {
8077
};
8178

8279
/**
83-
* @param {Array<DetectorSync>} [detectors]
84-
* @returns {Array<DetectorSync>}
80+
* @param {Array<ResourceDetector>} [detectors]
81+
* @returns {Array<ResourceDetector>}
8582
*/
8683
function resolveDetectors(detectors) {
8784
if (detectors) {
88-
detectors.push(distroDetectorSync);
85+
detectors.push(distroDetector);
8986
return detectors;
9087
}
9188

92-
let detectorKeys = getEnvVar('OTEL_NODE_RESOURCE_DETECTORS');
89+
let detectorKeys = getStringListFromEnv('OTEL_NODE_RESOURCE_DETECTORS') || [
90+
'all',
91+
];
9392
if (detectorKeys.some((k) => k === 'all')) {
9493
detectorKeys = Object.keys(defaultDetectors);
9594
} else if (detectorKeys.some((k) => k === 'none')) {
9695
return [];
9796
}
9897

99-
/** @type {Array<DetectorSync | DetectorSync[]>} */
100-
const resolvedDetectors = [distroDetectorSync];
98+
/** @type {Array<ResourceDetector | ResourceDetector[]>} */
99+
const resolvedDetectors = [distroDetector];
101100
for (const key of detectorKeys) {
102101
if (defaultDetectors[key]) {
103102
resolvedDetectors.push(defaultDetectors[key]);

packages/opentelemetry-node/lib/elastic-node-sdk.js

Lines changed: 21 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,17 @@
99

1010
const os = require('os');
1111

12+
const {
13+
getBooleanFromEnv,
14+
getStringFromEnv,
15+
getNumberFromEnv,
16+
} = require('@opentelemetry/core');
1217
const {metrics, NodeSDK, api} = require('@opentelemetry/sdk-node');
1318
const {BatchLogRecordProcessor} = require('@opentelemetry/sdk-logs');
1419

1520
const {log, registerOTelDiagLogger} = require('./logging');
1621
const {resolveDetectors} = require('./detectors');
17-
const {
18-
setupEnvironment,
19-
restoreEnvironment,
20-
getEnvVar,
21-
} = require('./environment');
22+
const {setupEnvironment, restoreEnvironment} = require('./environment');
2223
const {getInstrumentations} = require('./instrumentations');
2324
const {enableHostMetrics, HOST_METRICS_VIEWS} = require('./metrics/host');
2425
// @ts-ignore - compiler options do not allow lookp outside `lib` folder
@@ -32,9 +33,6 @@ class ElasticNodeSDK extends NodeSDK {
3233
log.trace('ElasticNodeSDK opts:', opts);
3334
registerOTelDiagLogger(api);
3435

35-
// Setup & fix some env
36-
setupEnvironment();
37-
3836
// - NodeSDK defaults to `TracerProviderWithEnvExporters` if neither
3937
// `spanProcessor` nor `traceExporter` are passed in.
4038
/** @type {Partial<NodeSDKConfiguration>} */
@@ -53,7 +51,8 @@ class ElasticNodeSDK extends NodeSDK {
5351
// Get logs exporter protocol based on environment.
5452
const logsExportProtocol =
5553
process.env.OTEL_EXPORTER_OTLP_LOGS_PROTOCOL ||
56-
getEnvVar('OTEL_EXPORTER_OTLP_PROTOCOL');
54+
getStringFromEnv('OTEL_EXPORTER_OTLP_PROTOCOL') ||
55+
'http/protobuf';
5756
let logExporterType = exporterPkgNameFromEnvVar[logsExportProtocol];
5857
if (!logExporterType) {
5958
log.warn(
@@ -77,12 +76,14 @@ class ElasticNodeSDK extends NodeSDK {
7776
// TODO what `temporalityPreference`?
7877

7978
// Disable metrics by config
80-
const metricsDisabled = getEnvVar('ELASTIC_OTEL_METRICS_DISABLED');
79+
const metricsDisabled =
80+
getBooleanFromEnv('ELASTIC_OTEL_METRICS_DISABLED') ?? false;
8181
if (!metricsDisabled) {
8282
// Get metrics exporter protocol based on environment.
8383
const metricsExportProtocol =
8484
process.env.OTEL_EXPORTER_OTLP_METRICS_PROTOCOL ||
85-
getEnvVar('OTEL_EXPORTER_OTLP_PROTOCOL');
85+
getStringFromEnv('OTEL_EXPORTER_OTLP_PROTOCOL') ||
86+
'http/protobuf';
8687
let metricExporterType =
8788
exporterPkgNameFromEnvVar[metricsExportProtocol];
8889
if (!metricExporterType) {
@@ -98,8 +99,12 @@ class ElasticNodeSDK extends NodeSDK {
9899
`@opentelemetry/exporter-metrics-otlp-${metricExporterType}`
99100
);
100101

101-
const metricsInterval = getEnvVar('OTEL_METRIC_EXPORT_INTERVAL');
102-
const metricsTimeout = getEnvVar('OTEL_METRIC_EXPORT_TIMEOUT');
102+
// Note: Default values has been taken from the specs
103+
// https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/#periodic-exporting-metricreader
104+
const metricsInterval =
105+
getNumberFromEnv('OTEL_METRIC_EXPORT_INTERVAL') ?? 60000;
106+
const metricsTimeout =
107+
getNumberFromEnv('OTEL_METRIC_EXPORT_TIMEOUT') ?? 30000;
103108
defaultConfig.metricReader =
104109
new metrics.PeriodicExportingMetricReader({
105110
exporter: new OTLPMetricExporter(),
@@ -112,6 +117,9 @@ class ElasticNodeSDK extends NodeSDK {
112117
];
113118
}
114119

120+
// Setup & fix some env
121+
setupEnvironment();
122+
115123
const configuration = Object.assign(defaultConfig, opts);
116124
super(configuration);
117125

packages/opentelemetry-node/lib/environment.js

Lines changed: 1 addition & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -3,55 +3,9 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6-
// NOTE: this API may be removed in future
7-
// ref: https://github.com/open-telemetry/opentelemetry-js/issues/5172
8-
const {getEnv} = require('@opentelemetry/core');
9-
106
/** @type {NodeJS.ProcessEnv} */
117
const envToRestore = {};
128

13-
/**
14-
* Returns an array of strings from the given input. If undefined returns the fallback
15-
* value.
16-
* @param {string | undefined} str
17-
* @param {string[]} [fallback=[]]
18-
* @returns {string[]}
19-
*/
20-
function parseStringList(str, fallback = []) {
21-
if (!str) {
22-
return fallback;
23-
}
24-
return str.split(',').map((s) => s.trim());
25-
}
26-
27-
/**
28-
* Returns a boolean from the given input
29-
* @param {string | undefined} str
30-
* @param {boolean} fallback
31-
* @returns {boolean}
32-
*/
33-
function parseBoolean(str, fallback) {
34-
if (!str) {
35-
return fallback;
36-
}
37-
return str.toLowerCase() === 'true';
38-
}
39-
40-
/**
41-
* Returns a boolean from te given input
42-
* @param {string | undefined} str
43-
* @param {number} fallback
44-
* @returns {number}
45-
*/
46-
function parseNumber(str, fallback) {
47-
if (!str) {
48-
return fallback;
49-
}
50-
51-
const num = Number(str);
52-
return isNaN(num) ? fallback : num;
53-
}
54-
559
/**
5610
* This funtion makes necessari changes to the environment so:
5711
* - Avoid OTEL's NodeSDK known warnings (eg. OTEL_TRACES_EXPORTER not set)
@@ -86,75 +40,11 @@ function setupEnvironment() {
8640
function restoreEnvironment() {
8741
Object.keys(envToRestore).forEach((k) => {
8842
process.env[k] = envToRestore[k];
43+
delete envToRestore[k];
8944
});
9045
}
9146

92-
/**
93-
* @typedef {Object} EdotEnv
94-
* @property {string[]} OTEL_NODE_RESOURCE_DETECTORS
95-
* @property {number} OTEL_METRIC_EXPORT_INTERVAL
96-
* @property {number} OTEL_METRIC_EXPORT_TIMEOUT
97-
* @property {boolean} ELASTIC_OTEL_METRICS_DISABLED
98-
*/
99-
/**
100-
* @typedef {keyof EdotEnv} EdotEnvKey
101-
*/
102-
/** @type {EdotEnv} */
103-
const edotEnv = {
104-
// Missing OTEL_ vars from global spec and nodejs specific spec
105-
OTEL_NODE_RESOURCE_DETECTORS: parseStringList(
106-
process.env.OTEL_NODE_RESOURCE_DETECTORS,
107-
['all']
108-
),
109-
// Note: Default values has been taken from the specs
110-
// https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/#periodic-exporting-metricreader
111-
OTEL_METRIC_EXPORT_INTERVAL: parseNumber(
112-
process.env.OTEL_METRIC_EXPORT_INTERVAL,
113-
60000
114-
),
115-
OTEL_METRIC_EXPORT_TIMEOUT: parseNumber(
116-
process.env.OTEL_METRIC_EXPORT_TIMEOUT,
117-
30000
118-
),
119-
// ELASTIC_OTEL_ vars
120-
ELASTIC_OTEL_METRICS_DISABLED: parseBoolean(
121-
process.env.ELASTIC_OTEL_METRICS_DISABLED,
122-
false
123-
),
124-
};
125-
126-
/**
127-
* @typedef {import('@opentelemetry/core').ENVIRONMENT} OtelEnv
128-
*/
129-
/**
130-
* @typedef {keyof OtelEnv} OtelEnvKey
131-
*/
132-
const otelEnv = getEnv();
133-
134-
/**
135-
* @template T
136-
* @typedef {T extends OtelEnvKey ? OtelEnv[T] : T extends EdotEnvKey ? EdotEnv[T] : never} EnvValue<T>
137-
*/
138-
/**
139-
* @template {OtelEnvKey | EdotEnvKey} T
140-
* Returns the value of the env var already parsed to the proper type. If
141-
* the variable is not defined it will return the default value based on
142-
* the environmment variables spec https://opentelemetry.io/docs/specs/otel/configuration/sdk-environment-variables/
143-
* @param {T} name
144-
* @returns {EnvValue<T>}
145-
*/
146-
function getEnvVar(name) {
147-
if (name in otelEnv) {
148-
// @ts-ignore -- T is {keyof OtelEnv} but not sure how to make TS infer that
149-
return otelEnv[name];
150-
}
151-
152-
// @ts-ignore -- T is {keyof EdotEnv} but not sure how to make TS infer that
153-
return edotEnv[name];
154-
}
155-
15647
module.exports = {
15748
setupEnvironment,
15849
restoreEnvironment,
159-
getEnvVar,
16050
};

packages/opentelemetry-node/lib/instrumentations.js

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,12 @@
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

6+
const {
7+
getStringListFromEnv,
8+
getBooleanFromEnv,
9+
} = require('@opentelemetry/core');
10+
const {log} = require('./logging');
11+
612
/**
713
* @typedef {import('@opentelemetry/instrumentation').Instrumentation} Instrumentation
814
*
@@ -95,9 +101,6 @@ const {TediousInstrumentation} = require('@opentelemetry/instrumentation-tedious
95101
const {UndiciInstrumentation} = require('@opentelemetry/instrumentation-undici');
96102
const {WinstonInstrumentation} = require('@opentelemetry/instrumentation-winston');
97103

98-
const {log} = require('./logging');
99-
const {getEnvVar} = require('./environment');
100-
101104
// Instrumentations attach their Hook (for require-in-the-middle or import-in-the-middle)
102105
// when the `enable` method is called and this happens inside their constructor
103106
// https://github.com/open-telemetry/opentelemetry-js/blob/1b4999f386e0240b7f65350e8360ccc2930b0fe6/experimental/packages/opentelemetry-instrumentation/src/platform/node/instrumentation.ts#L71
@@ -112,7 +115,7 @@ const instrumentationsMap = {
112115
'@opentelemetry/instrumentation-aws-sdk': (cfg) => new AwsInstrumentation(cfg),
113116
'@opentelemetry/instrumentation-bunyan': (cfg) => new BunyanInstrumentation(cfg),
114117
'@opentelemetry/instrumentation-connect': (cfg) => new ConnectInstrumentation(cfg),
115-
'@opentelemetry/instrumentation-cassandra-driver': (cfg) => new CassandraDriverInstrumentation(cfg),
118+
'@opentelemetry/instrumentation-cassandra-driver': (cfg) => new CassandraDriverInstrumentation(cfg),
116119
'@opentelemetry/instrumentation-cucumber': (cfg) => new CucumberInstrumentation(cfg),
117120
'@opentelemetry/instrumentation-dataloader': (cfg) => new DataloaderInstrumentation(cfg),
118121
'@opentelemetry/instrumentation-dns': (cfg) => new DnsInstrumentation(cfg),
@@ -179,9 +182,9 @@ for (const name of Object.keys(instrumentationsMap)) {
179182
* @returns {Array<string> | undefined}
180183
*/
181184
function getInstrumentationsFromEnv(envvar) {
182-
if (process.env[envvar]) {
185+
const names = getStringListFromEnv(envvar);
186+
if (names) {
183187
const instrumentations = [];
184-
const names = process.env[envvar].split(',').map((s) => s.trim());
185188

186189
for (const name of names) {
187190
if (otelInstrShortNames.has(name)) {
@@ -267,7 +270,8 @@ function getInstrumentations(opts = {}) {
267270
}
268271

269272
// Skip if metrics are disabled by env var
270-
const isMetricsDisabled = getEnvVar('ELASTIC_OTEL_METRICS_DISABLED');
273+
const isMetricsDisabled =
274+
getBooleanFromEnv('ELASTIC_OTEL_METRICS_DISABLED') ?? false;
271275
if (
272276
isMetricsDisabled &&
273277
name === '@opentelemetry/instrumentation-runtime-node'

0 commit comments

Comments
 (0)