Skip to content

Commit 804c2c2

Browse files
committed
feat: upgrade to OTel SDK 2.0.0, extend support down to Node.js 16
Use OpenTelemetry JS SDK 2.x and matching related packages for Node.js versions >= 18.19.0. Fall back to SDK 1.x and matching related packages for Node.js versions >= 16.0.0 and < 18.19.0. This release extends the range of supported versions down to Node.js 16.x (previously only Node.js >= 18.x had been supported). BREAKING CHANGE: Due to the automatic fallback to SDK 1.x for older Node.js versions, there is no actual breaking change in this release. This comment just serves the purpose of forcing semantic-release to publish this as version 2.0.0 instead of 1.2.0, which seems warranted for the upgrade to OTel JS SDK 2.x. Strictly speaking, the fact that Node.js 16.x processes will now be automatically instrumented as well might be seen as a breaking change.
1 parent ebc489b commit 804c2c2

File tree

32 files changed

+4800
-670
lines changed

32 files changed

+4800
-670
lines changed

.github/workflows/verify.yaml

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,16 @@ jobs:
1414

1515
strategy:
1616
matrix:
17-
node-version: [ '18.x', '20.x', '21.x', '22.x' ]
17+
node-version:
18+
- '16.x'
19+
# 18.18.2 is the most recent version that is _not_ supported by OpenTelemetry JS SDK 2.x, hence we test it
20+
# individually. (Should run with OpenTelemetry JS SDK 1.x.)
21+
- '18.18.2'
22+
# 18.x evaluates to a 18.x line >= 18.19.0, and hence will be run with OpenTelemetry JS SDK 2.x.
23+
- '18.x'
24+
- '20.x'
25+
- '22.x'
26+
- '23.x'
1827

1928
steps:
2029
- uses: actions/checkout@v4
@@ -33,6 +42,7 @@ jobs:
3342
- run: npm ci
3443

3544
- name: lint
45+
if: ${{ matrix.node-version != '16.x' }}
3646
run: |
3747
npm run lint
3848
@@ -46,7 +56,8 @@ jobs:
4656

4757
strategy:
4858
matrix:
49-
node-version: [ '16.x' ]
59+
node-version:
60+
- '14.x'
5061

5162
steps:
5263
- uses: actions/checkout@v4
@@ -62,11 +73,11 @@ jobs:
6273
node --version
6374
npm --version
6475
65-
- run: npm ci
76+
- run: npm install
6677

67-
- name: integration test
78+
- name: minimum version integration test
6879
run: |
69-
npm run test:integration
80+
npm run test:integration:minimum-version-check
7081
7182
publish-release:
7283
name: Publish Release

.nvmrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
20
1+
22

package-lock.json

Lines changed: 3622 additions & 582 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
"prettier-check": "prettier --check eslint.config.js 'src/**/*.[jt]s' 'test/**/*.[jt]s' --parser typescript",
1515
"test": "npm run test:unit && npm run test:integration",
1616
"test:unit": "mocha --config test/.mocharc.unit.js --require ts-node/register",
17-
"test:integration": "mocha --config test/.mocharc.integration.js --require ts-node/register --require test/integration/rootHooks.ts",
17+
"test:integration": "npm run test:integration:default && npm run test:integration:minimum-version-check",
18+
"test:integration:default": "mocha --config test/.mocharc.integration.js --require ts-node/register --require test/integration/rootHooks.ts",
19+
"test:integration:minimum-version-check": "mocha --config test/.mocharc.integration.minimum-version-check.js --require ts-node/register --require test/integration/rootHooks.ts",
1820
"test:coverage": "nyc npm run test",
1921
"start-collector": "node --require ts-node/register test/collector/index.ts"
2022
},
@@ -54,18 +56,31 @@
5456
"homepage": "https://github.com/dash0hq/opentelemetry-js-distribution#readme",
5557
"dependencies": {
5658
"@opentelemetry/api": "^1.9.0",
57-
"@opentelemetry/auto-instrumentations-node": "^0.57.0",
59+
"@opentelemetry/auto-instrumentations-node": "^0.58.0",
60+
"@opentelemetry/auto-instrumentations-node-1.x": "npm:@opentelemetry/[email protected]",
61+
"@opentelemetry/core": "^2.0.0",
62+
"@opentelemetry/core-1.x": "npm:@opentelemetry/[email protected]",
5863
"@opentelemetry/exporter-logs-otlp-proto": "^0.200.0",
64+
"@opentelemetry/exporter-logs-otlp-proto-1.x": "npm:@opentelemetry/[email protected]",
5965
"@opentelemetry/exporter-metrics-otlp-proto": "^0.200.0",
66+
"@opentelemetry/exporter-metrics-otlp-proto-1.x": "npm:@opentelemetry/[email protected]",
6067
"@opentelemetry/exporter-trace-otlp-proto": "^0.200.0",
61-
"@opentelemetry/instrumentation-kafkajs": "^0.8.0",
68+
"@opentelemetry/exporter-trace-otlp-proto-1.x": "npm:@opentelemetry/[email protected]",
69+
"@opentelemetry/instrumentation-kafkajs": "^0.9.0",
6270
"@opentelemetry/resource-detector-container": "^0.7.0",
71+
"@opentelemetry/resource-detector-container-1.x": "npm:@opentelemetry/[email protected]",
6372
"@opentelemetry/resources": "^2.0.0",
73+
"@opentelemetry/resources-1.x": "npm:@opentelemetry/[email protected]",
6474
"@opentelemetry/sdk-logs": "^0.200.0",
75+
"@opentelemetry/sdk-logs-1.x": "npm:@opentelemetry/[email protected]",
6576
"@opentelemetry/sdk-metrics": "^2.0.0",
77+
"@opentelemetry/sdk-metrics-1.x": "npm:@opentelemetry/[email protected]",
6678
"@opentelemetry/sdk-node": "^0.200.0",
79+
"@opentelemetry/sdk-node-1.x": "npm:@opentelemetry/[email protected]",
6780
"@opentelemetry/sdk-trace-base": "^2.0.0",
68-
"@opentelemetry/sdk-trace-node": "^2.0.0"
81+
"@opentelemetry/sdk-trace-base-1.x": "npm:@opentelemetry/[email protected]",
82+
"@opentelemetry/sdk-trace-node": "^2.0.0",
83+
"@opentelemetry/sdk-trace-node-1.x": "npm:@opentelemetry/[email protected]"
6984
},
7085
"devDependencies": {
7186
"@eslint/js": "^9.6.0",
@@ -84,6 +99,7 @@
8499
"eslint-plugin-unused-imports": "^3.2.0",
85100
"is-ci": "^4.1.0",
86101
"mocha": "^11.0.1",
102+
"node-fetch": "^2.7.0",
87103
"nyc": "^17.0.0",
88104
"prettier": "^3.3.2",
89105
"protobufjs": "^7.3.2",

src/detectors/node/opentelemetry-resource-detector-kubernetes-pod/index.ts renamed to src/1.x/detectors/node/opentelemetry-resource-detector-kubernetes-pod/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
// SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc.
22
// SPDX-License-Identifier: Apache-2.0
33

4-
import { DetectorSync, Resource } from '@opentelemetry/resources';
5-
import { ResourceAttributes } from '@opentelemetry/resources/build/src/types';
4+
import { DetectorSync, Resource } from '@opentelemetry/resources-1.x';
5+
import { ResourceAttributes } from '@opentelemetry/resources-1.x/build/src/types';
66
import { SEMRESATTRS_K8S_POD_UID } from '@opentelemetry/semantic-conventions';
77
import { readFile } from 'node:fs/promises';
88
import os from 'os';

src/detectors/node/opentelemetry-resource-detector-kubernetes-pod/index_test.ts renamed to src/1.x/detectors/node/opentelemetry-resource-detector-kubernetes-pod/index_test.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc.
22
// SPDX-License-Identifier: Apache-2.0
33

4-
import { Resource } from '@opentelemetry/resources';
4+
import { Resource } from '@opentelemetry/resources-1.x';
55
import { SEMRESATTRS_K8S_POD_UID } from '@opentelemetry/semantic-conventions';
66
import { expect } from 'chai';
77
import fs from 'node:fs/promises';

src/detectors/node/opentelemetry-resource-detector-service-name-fallback/index.ts renamed to src/1.x/detectors/node/opentelemetry-resource-detector-service-name-fallback/index.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
// SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc.
22
// SPDX-License-Identifier: Apache-2.0
33

4-
import { DetectorSync, Resource } from '@opentelemetry/resources';
4+
import { DetectorSync, Resource } from '@opentelemetry/resources-1.x';
55
import { SEMRESATTRS_SERVICE_NAME, SEMRESATTRS_SERVICE_VERSION } from '@opentelemetry/semantic-conventions';
66

7-
import { readPackageJson } from './packageJsonUtil';
7+
import { readPackageJson } from '../../../../util/packageJsonUtil';
88

99
export default class ServiceNameFallbackDetector implements DetectorSync {
1010
detect(): Resource {

src/detectors/node/opentelemetry-resource-detector-service-name-fallback/index_test.ts renamed to src/1.x/detectors/node/opentelemetry-resource-detector-service-name-fallback/index_test.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
// SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc.
22
// SPDX-License-Identifier: Apache-2.0
33

4-
import { Resource } from '@opentelemetry/resources';
4+
import { Resource } from '@opentelemetry/resources-1.x';
55
import { SEMRESATTRS_SERVICE_NAME, SEMRESATTRS_SERVICE_VERSION } from '@opentelemetry/semantic-conventions';
66
import { expect } from 'chai';
77
import Sinon from 'sinon';
88
import sinon from 'sinon';
99

1010
import ServiceNameFallbackDetector from './index';
11-
import * as packageJsonUtil from './packageJsonUtil';
11+
import * as packageJsonUtil from '../../../../util/packageJsonUtil';
1212

1313
const packageJson = {
1414
name: '@example/app-under-test',

src/init.ts renamed to src/1.x/init.ts

Lines changed: 28 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,30 @@
22
// SPDX-License-Identifier: Apache-2.0
33

44
import { SpanKind, trace } from '@opentelemetry/api';
5-
import { getNodeAutoInstrumentations, getResourceDetectors } from '@opentelemetry/auto-instrumentations-node';
6-
import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-proto';
7-
import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-proto';
8-
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';
9-
import { containerDetector } from '@opentelemetry/resource-detector-container';
10-
import { Detector, DetectorSync, envDetector, hostDetector, processDetector, Resource } from '@opentelemetry/resources';
11-
import { BatchLogRecordProcessor } from '@opentelemetry/sdk-logs';
12-
import { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';
13-
import { NodeSDK, NodeSDKConfiguration } from '@opentelemetry/sdk-node';
14-
import { BatchSpanProcessor, SpanProcessor } from '@opentelemetry/sdk-trace-base';
15-
import { ConsoleSpanExporter } from '@opentelemetry/sdk-trace-node';
5+
import { getNodeAutoInstrumentations, getResourceDetectors } from '@opentelemetry/auto-instrumentations-node-1.x';
6+
import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-proto-1.x';
7+
import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-proto-1.x';
8+
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto-1.x';
9+
import { containerDetector } from '@opentelemetry/resource-detector-container-1.x';
10+
import {
11+
Detector,
12+
DetectorSync,
13+
envDetector,
14+
hostDetector,
15+
processDetector,
16+
Resource,
17+
} from '@opentelemetry/resources-1.x';
18+
import { BatchLogRecordProcessor } from '@opentelemetry/sdk-logs-1.x';
19+
import { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics-1.x';
20+
import { NodeSDK, NodeSDKConfiguration } from '@opentelemetry/sdk-node-1.x';
21+
import { BatchSpanProcessor, SpanProcessor } from '@opentelemetry/sdk-trace-base-1.x';
22+
import { ConsoleSpanExporter } from '@opentelemetry/sdk-trace-node-1.x';
1623

1724
import PodUidDetector from './detectors/node/opentelemetry-resource-detector-kubernetes-pod';
1825
import ServiceNameFallbackDetector from './detectors/node/opentelemetry-resource-detector-service-name-fallback';
1926
import { FileSpanExporter } from './util/FileSpanExporter';
20-
import { hasOptedIn, hasOptedOut, parseNumericEnvironmentVariableWithDefault } from './util/environment';
21-
import { kafkaJsInstrumentation } from './util/kafkajs';
27+
import { hasOptedIn, hasOptedOut, parseNumericEnvironmentVariableWithDefault } from '../util/environment';
28+
import { kafkaJsInstrumentation } from '../util/kafkajs';
2229

2330
const logPrefix = 'Dash0 OpenTelemetry distribution for Node.js:';
2431
const debugOutput = hasOptedIn('DASH0_DEBUG');
@@ -61,8 +68,16 @@ let sdkShutdownHasBeenCalled = false;
6168
const baseUrl = process.env.DASH0_OTEL_COLLECTOR_BASE_URL;
6269

6370
const configuration: Partial<NodeSDKConfiguration> = {
71+
// Ignore TS2322 triggered by different import paths due to custom package aliases being used in package.json
72+
// ("@opentelemetry/sdk-trace-base-1.x": "npm:@opentelemetry/[email protected]" etc.)
73+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
74+
// @ts-ignore
6475
spanProcessors: spanProcessors(),
76+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
77+
// @ts-ignore
6578
metricReader: metricsReader(),
79+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
80+
// @ts-ignore
6681
logRecordProcessor: logRecordProcessor(),
6782
instrumentations: [
6883
//

src/1.x/util/FileSpanExporter.ts

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
// SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
import { FileHandle, open } from 'node:fs/promises';
5+
import { EOL } from 'os';
6+
7+
import { ExportResult, ExportResultCode, hrTimeToMicroseconds } from '@opentelemetry/core-1.x';
8+
import { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base-1.x';
9+
10+
/**
11+
* This is implementation of {@link SpanExporter} that prints spans to a file.
12+
*/
13+
export class FileSpanExporter implements SpanExporter {
14+
private file: Promise<FileHandle>;
15+
16+
constructor(filename: string) {
17+
this.file = open(filename, 'a');
18+
this.tryOpen(filename);
19+
}
20+
21+
async tryOpen(filename: string) {
22+
try {
23+
await this.file;
24+
} catch (e: any) {
25+
console.error(
26+
`Opening the file ${filename} (set via DASH0_DEBUG_PRINT_SPANS) for writing/appending failed. No spans will be written to that file.`,
27+
e,
28+
);
29+
}
30+
}
31+
32+
export(spans: ReadableSpan[], resultCallback: (result: ExportResult) => void): void {
33+
this.printAndReportResult(spans, resultCallback);
34+
}
35+
36+
shutdown(): Promise<void> {
37+
this.printAndReportResult([]);
38+
return this.forceFlush();
39+
}
40+
41+
forceFlush(): Promise<void> {
42+
return Promise.resolve();
43+
}
44+
45+
// mimics ConsoleSpanExporter._exportInfo
46+
private serialize(span: ReadableSpan) {
47+
return JSON.stringify({
48+
resource: {
49+
attributes: span.resource.attributes,
50+
},
51+
traceId: span.spanContext().traceId,
52+
parentId: span.parentSpanId,
53+
traceState: span.spanContext().traceState?.serialize(),
54+
name: span.name,
55+
id: span.spanContext().spanId,
56+
kind: span.kind,
57+
timestamp: hrTimeToMicroseconds(span.startTime),
58+
duration: hrTimeToMicroseconds(span.duration),
59+
attributes: span.attributes,
60+
status: span.status,
61+
events: span.events,
62+
links: span.links,
63+
});
64+
}
65+
66+
private async printAndReportResult(spans: ReadableSpan[], done?: (result: ExportResult) => void): Promise<void> {
67+
// This is only asynchronous the first time it is called (and that only if it is called before the open call
68+
// happening in the constructor has finished), after that, this.file is a resolved promise.
69+
let fileHandle;
70+
try {
71+
fileHandle = await this.file;
72+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
73+
} catch (e) {
74+
if (done) {
75+
done({ code: ExportResultCode.FAILED });
76+
}
77+
return;
78+
}
79+
80+
for (const span of spans) {
81+
await fileHandle.appendFile(this.serialize(span));
82+
await fileHandle.appendFile(EOL);
83+
}
84+
if (done) {
85+
done({ code: ExportResultCode.SUCCESS });
86+
}
87+
}
88+
}

0 commit comments

Comments
 (0)