Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
21 changes: 16 additions & 5 deletions .github/workflows/verify.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,16 @@ jobs:

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

steps:
- uses: actions/checkout@v4
Expand All @@ -33,6 +42,7 @@ jobs:
- run: npm ci

- name: lint
if: ${{ matrix.node-version != '16.x' }}
run: |
npm run lint

Expand All @@ -46,7 +56,8 @@ jobs:

strategy:
matrix:
node-version: [ '16.x' ]
node-version:
- '14.x'

steps:
- uses: actions/checkout@v4
Expand All @@ -62,11 +73,11 @@ jobs:
node --version
npm --version

- run: npm ci
- run: npm install

- name: integration test
- name: minimum version integration test
run: |
npm run test:integration
npm run test:integration:minimum-version-check

publish-release:
name: Publish Release
Expand Down
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
20
22
3,702 changes: 3,370 additions & 332 deletions package-lock.json

Large diffs are not rendered by default.

42 changes: 29 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@
"prettier-check": "prettier --check eslint.config.js 'src/**/*.[jt]s' 'test/**/*.[jt]s' --parser typescript",
"test": "npm run test:unit && npm run test:integration",
"test:unit": "mocha --config test/.mocharc.unit.js --require ts-node/register",
"test:integration": "mocha --config test/.mocharc.integration.js --require ts-node/register --require test/integration/rootHooks.ts",
"test:integration": "npm run test:integration:default && npm run test:integration:minimum-version-check",
"test:integration:default": "mocha --config test/.mocharc.integration.js --require ts-node/register --require test/integration/rootHooks.ts",
"test:integration:minimum-version-check": "mocha --config test/.mocharc.integration.minimum-version-check.js --require ts-node/register --require test/integration/rootHooks.ts",
"test:coverage": "nyc npm run test",
"start-collector": "node --require ts-node/register test/collector/index.ts"
},
Expand Down Expand Up @@ -54,18 +56,31 @@
"homepage": "https://github.com/dash0hq/opentelemetry-js-distribution#readme",
"dependencies": {
"@opentelemetry/api": "^1.9.0",
"@opentelemetry/auto-instrumentations-node": "^0.56.0",
"@opentelemetry/exporter-logs-otlp-proto": "^0.57.0",
"@opentelemetry/exporter-metrics-otlp-proto": "^0.57.0",
"@opentelemetry/exporter-trace-otlp-proto": "^0.57.0",
"@opentelemetry/instrumentation-kafkajs": "^0.7.0",
"@opentelemetry/resource-detector-container": "^0.6.0",
"@opentelemetry/resources": "^1.25.1",
"@opentelemetry/sdk-logs": "^0.57.0",
"@opentelemetry/sdk-metrics": "^1.25.1",
"@opentelemetry/sdk-node": "^0.57.0",
"@opentelemetry/sdk-trace-base": "^1.25.1",
"@opentelemetry/sdk-trace-node": "^1.25.1"
"@opentelemetry/auto-instrumentations-node": "^0.58.0",
"@opentelemetry/auto-instrumentations-node-1.x": "npm:@opentelemetry/[email protected]",
"@opentelemetry/core": "^2.0.0",
"@opentelemetry/core-1.x": "npm:@opentelemetry/[email protected]",
"@opentelemetry/exporter-logs-otlp-proto": "^0.200.0",
"@opentelemetry/exporter-logs-otlp-proto-1.x": "npm:@opentelemetry/[email protected]",
"@opentelemetry/exporter-metrics-otlp-proto": "^0.200.0",
"@opentelemetry/exporter-metrics-otlp-proto-1.x": "npm:@opentelemetry/[email protected]",
"@opentelemetry/exporter-trace-otlp-proto": "^0.200.0",
"@opentelemetry/exporter-trace-otlp-proto-1.x": "npm:@opentelemetry/[email protected]",
"@opentelemetry/instrumentation-kafkajs": "^0.9.0",
"@opentelemetry/resource-detector-container": "^0.7.0",
"@opentelemetry/resource-detector-container-1.x": "npm:@opentelemetry/[email protected]",
"@opentelemetry/resources": "^2.0.0",
"@opentelemetry/resources-1.x": "npm:@opentelemetry/[email protected]",
"@opentelemetry/sdk-logs": "^0.200.0",
"@opentelemetry/sdk-logs-1.x": "npm:@opentelemetry/[email protected]",
"@opentelemetry/sdk-metrics": "^2.0.0",
"@opentelemetry/sdk-metrics-1.x": "npm:@opentelemetry/[email protected]",
"@opentelemetry/sdk-node": "^0.200.0",
"@opentelemetry/sdk-node-1.x": "npm:@opentelemetry/[email protected]",
"@opentelemetry/sdk-trace-base": "^2.0.0",
"@opentelemetry/sdk-trace-base-1.x": "npm:@opentelemetry/[email protected]",
"@opentelemetry/sdk-trace-node": "^2.0.0",
"@opentelemetry/sdk-trace-node-1.x": "npm:@opentelemetry/[email protected]"
},
"devDependencies": {
"@eslint/js": "^9.6.0",
Expand All @@ -84,6 +99,7 @@
"eslint-plugin-unused-imports": "^3.2.0",
"is-ci": "^4.1.0",
"mocha": "^11.0.1",
"node-fetch": "^2.7.0",
"nyc": "^17.0.0",
"prettier": "^3.3.2",
"protobufjs": "^7.3.2",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc.
// SPDX-License-Identifier: Apache-2.0

import { DetectorSync, Resource } from '@opentelemetry/resources';
import { ResourceAttributes } from '@opentelemetry/resources/build/src/types';
import { DetectorSync, Resource } from '@opentelemetry/resources-1.x';
import { ResourceAttributes } from '@opentelemetry/resources-1.x/build/src/types';
import { SEMRESATTRS_K8S_POD_UID } from '@opentelemetry/semantic-conventions';
import { readFile } from 'node:fs/promises';
import os from 'os';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc.
// SPDX-License-Identifier: Apache-2.0

import { Resource } from '@opentelemetry/resources';
import { Resource } from '@opentelemetry/resources-1.x';
import { SEMRESATTRS_K8S_POD_UID } from '@opentelemetry/semantic-conventions';
import { expect } from 'chai';
import fs from 'node:fs/promises';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
// SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc.
// SPDX-License-Identifier: Apache-2.0

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

import { readPackageJson } from './packageJsonUtil';
import { readPackageJson } from '../../../../util/packageJsonUtil';

export default class ServiceNameFallbackDetector implements DetectorSync {
detect(): Resource {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
// SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc.
// SPDX-License-Identifier: Apache-2.0

import { Resource } from '@opentelemetry/resources';
import { Resource } from '@opentelemetry/resources-1.x';
import { SEMRESATTRS_SERVICE_NAME, SEMRESATTRS_SERVICE_VERSION } from '@opentelemetry/semantic-conventions';
import { expect } from 'chai';
import Sinon from 'sinon';
import sinon from 'sinon';

import ServiceNameFallbackDetector from './index';
import * as packageJsonUtil from './packageJsonUtil';
import * as packageJsonUtil from '../../../../util/packageJsonUtil';

const packageJson = {
name: '@example/app-under-test',
Expand Down
41 changes: 28 additions & 13 deletions src/init.ts → src/1.x/init.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,30 @@
// SPDX-License-Identifier: Apache-2.0

import { SpanKind, trace } from '@opentelemetry/api';
import { getNodeAutoInstrumentations, getResourceDetectors } from '@opentelemetry/auto-instrumentations-node';
import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-proto';
import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-proto';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto';
import { containerDetector } from '@opentelemetry/resource-detector-container';
import { Detector, DetectorSync, envDetector, hostDetector, processDetector, Resource } from '@opentelemetry/resources';
import { BatchLogRecordProcessor } from '@opentelemetry/sdk-logs';
import { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';
import { NodeSDK, NodeSDKConfiguration } from '@opentelemetry/sdk-node';
import { BatchSpanProcessor, SpanProcessor } from '@opentelemetry/sdk-trace-base';
import { ConsoleSpanExporter } from '@opentelemetry/sdk-trace-node';
import { getNodeAutoInstrumentations, getResourceDetectors } from '@opentelemetry/auto-instrumentations-node-1.x';
import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-proto-1.x';
import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-proto-1.x';
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-proto-1.x';
import { containerDetector } from '@opentelemetry/resource-detector-container-1.x';
import {
Detector,
DetectorSync,
envDetector,
hostDetector,
processDetector,
Resource,
} from '@opentelemetry/resources-1.x';
import { BatchLogRecordProcessor } from '@opentelemetry/sdk-logs-1.x';
import { PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics-1.x';
import { NodeSDK, NodeSDKConfiguration } from '@opentelemetry/sdk-node-1.x';
import { BatchSpanProcessor, SpanProcessor } from '@opentelemetry/sdk-trace-base-1.x';
import { ConsoleSpanExporter } from '@opentelemetry/sdk-trace-node-1.x';

import PodUidDetector from './detectors/node/opentelemetry-resource-detector-kubernetes-pod';
import ServiceNameFallbackDetector from './detectors/node/opentelemetry-resource-detector-service-name-fallback';
import { FileSpanExporter } from './util/FileSpanExporter';
import { hasOptedIn, hasOptedOut, parseNumericEnvironmentVariableWithDefault } from './util/environment';
import { kafkaJsInstrumentation } from './util/kafkajs';
import { hasOptedIn, hasOptedOut, parseNumericEnvironmentVariableWithDefault } from '../util/environment';
import { kafkaJsInstrumentation } from '../util/kafkajs';

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

const configuration: Partial<NodeSDKConfiguration> = {
// Ignore TS2322 triggered by different import paths due to custom package aliases being used in package.json
// ("@opentelemetry/sdk-trace-base-1.x": "npm:@opentelemetry/[email protected]" etc.)
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
spanProcessors: spanProcessors(),
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
metricReader: metricsReader(),
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
logRecordProcessor: logRecordProcessor(),
instrumentations: [
//
Expand Down
88 changes: 88 additions & 0 deletions src/1.x/util/FileSpanExporter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// SPDX-FileCopyrightText: Copyright 2024 Dash0 Inc.
// SPDX-License-Identifier: Apache-2.0

import { FileHandle, open } from 'node:fs/promises';
import { EOL } from 'os';

import { ExportResult, ExportResultCode, hrTimeToMicroseconds } from '@opentelemetry/core-1.x';
import { ReadableSpan, SpanExporter } from '@opentelemetry/sdk-trace-base-1.x';

/**
* This is implementation of {@link SpanExporter} that prints spans to a file.
*/
export class FileSpanExporter implements SpanExporter {
private file: Promise<FileHandle>;

constructor(filename: string) {
this.file = open(filename, 'a');
this.tryOpen(filename);
}

async tryOpen(filename: string) {
try {
await this.file;
} catch (e: any) {
console.error(
`Opening the file ${filename} (set via DASH0_DEBUG_PRINT_SPANS) for writing/appending failed. No spans will be written to that file.`,
e,
);
}
}

export(spans: ReadableSpan[], resultCallback: (result: ExportResult) => void): void {
this.printAndReportResult(spans, resultCallback);
}

shutdown(): Promise<void> {
this.printAndReportResult([]);
return this.forceFlush();
}

forceFlush(): Promise<void> {
return Promise.resolve();
}

// mimics ConsoleSpanExporter._exportInfo
private serialize(span: ReadableSpan) {
return JSON.stringify({
resource: {
attributes: span.resource.attributes,
},
traceId: span.spanContext().traceId,
parentId: span.parentSpanId,
traceState: span.spanContext().traceState?.serialize(),
name: span.name,
id: span.spanContext().spanId,
kind: span.kind,
timestamp: hrTimeToMicroseconds(span.startTime),
duration: hrTimeToMicroseconds(span.duration),
attributes: span.attributes,
status: span.status,
events: span.events,
links: span.links,
});
}

private async printAndReportResult(spans: ReadableSpan[], done?: (result: ExportResult) => void): Promise<void> {
// This is only asynchronous the first time it is called (and that only if it is called before the open call
// happening in the constructor has finished), after that, this.file is a resolved promise.
let fileHandle;
try {
fileHandle = await this.file;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
} catch (e) {
if (done) {
done({ code: ExportResultCode.FAILED });
}
return;
}

for (const span of spans) {
await fileHandle.appendFile(this.serialize(span));
await fileHandle.appendFile(EOL);
}
if (done) {
done({ code: ExportResultCode.SUCCESS });
}
}
}
Loading