Skip to content

Comments

otel: add new module#61907

Open
bengl wants to merge 1 commit intonodejs:mainfrom
bengl:bengl/otel
Open

otel: add new module#61907
bengl wants to merge 1 commit intonodejs:mainfrom
bengl:bengl/otel

Conversation

@bengl
Copy link
Member

@bengl bengl commented Feb 20, 2026

Adds support for OpenTelemetry tracing, as described in the included docs. Custom instrumentation is not yet supported.

This PR is intended to lay the groundwork for future PRs where API support for custom instrumentations is added. The OpenTelemetry SDK is explicitly not used, mainly to avoid bloat.

A programmatic API to enable and disable tracing is added as a new module, node:otel, but I'm very open to suggestions in terms of where to add it instead, keeping in mind that the intent is to eventually expose an API for custom instrumentation alongside it.

Disclaimer: As an experiment, Claude Code was used to build this PR, based on a hand-written prototype I built (but did not publish) about a year ago. Everything was thoroughly reviewed by me prior to PR submission, including some hand-edits.

Adds support for OpenTelemetry tracing, as described in the included
docs.

Custom instrumentation is not yet supported.
@nodejs-github-bot
Copy link
Collaborator

Review requested:

  • @nodejs/config
  • @nodejs/loaders
  • @nodejs/startup

@nodejs-github-bot nodejs-github-bot added lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run. labels Feb 20, 2026
Copy link
Member

@legendecas legendecas left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AFAICT, it would create confusion that the built-in support does not work with the @opentelemetry/api package, which is intended to be the global provider for otel components, like propagators and exporters.

@mcollina
Copy link
Member

I personally find the opentelemetry apis really cumbersome to work with. Maybe there is something better that can be done.

But I also concur that some kind of high level compatibility api would need to exist.

@ljharb
Copy link
Member

ljharb commented Feb 20, 2026

It would be really nice to get ahold of https://www.npmjs.com/package/otel and add this new core module without the prefix.

otel.start({ endpoint: 'http://collector.example.com:4318' });

// ... application code ...
otel.stop();
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More a style nit... feel free to ignore. I find this start()/stop() pattern a bit cumbersome. I'd be happier something like...

const stop = otel.start();

stop();

which also allows for...

{
  using session = otel.start();
  // automatically stops
}

* `options` {Object}
* `endpoint` {string} The OTLP collector endpoint URL.
**Default:** `'http://localhost:4318'`.
* `filter` {string|string\[]} Optional comma-separated string or array of
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: can we pick one? I'd prefer just string[].

@legendecas
Copy link
Member

legendecas commented Feb 20, 2026

This PR is intended to lay the groundwork for future PRs where API support for custom instrumentations is added. The OpenTelemetry SDK is explicitly not used, mainly to avoid bloat.
keeping in mind that the intent is to eventually expose an API for custom instrumentation alongside it.

Agree that an OpenTelemetry SDK is not built-in to the Node.js instrumentation. However, this implementation is not fully compliant with the OpenTelemetry design. The instrumentation should not implement the OpenTelemetry SDK components, like exporters and tracer SDK.

The three OpenTelemetry layers, API, SDK, and instrumentations, have their clear scopes and allows open customization without any vendor lock-in. That is, instrumentations will use the API to create spans and metrics, but do not directly depend on the SDK. This allows instrumentations to be vendor-agnostic. And observability vendors could provide their SDK (or just an exporter) to do whatever propriatary works, like exporting.

I think a Node.js built-in otel module should provide OpenTelemetry specification compliant API and implementations. A built-in OpenTelemetry should not split the OpenTelemetry community.

Copy link
Member

@Qard Qard left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some small questions/ideas, but otherwise LGTM!

### `OTEL_SERVICE_NAME`

Standard OpenTelemetry environment variable used to set the service name in
exported resource attributes. Defaults to `node-<pid>`.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We already have to load the package.json during startup, don't we? So perhaps we could make this use the name field from it, if present, and fallback to that?

Comment on lines +39 to +54
let timeOriginNs;
function getTimeOriginNs() {
if (timeOriginNs === undefined) {
// getTimeOriginTimestamp() returns ms since Unix epoch.
// Multiply by 1e6 to get nanoseconds.
const originMs = getTimeOriginTimestamp();
timeOriginNs = BigInt(MathRound(originMs * 1e6));
}
return timeOriginNs;
}

function hrTimeToNanos() {
const relativeMs = now();
const ns = getTimeOriginNs() + BigInt(MathRound(relativeMs * 1e6));
return `${ns}`;
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm wondering a bit about the perf of this. Did you try a high/low pair like process.hrtime() returns to be able to do the 64-bit timestamp rather than this string approach? I'm a bit worried about the perf implications of lots of bigint timestamps being generated all the time. 🤔

@codecov
Copy link

codecov bot commented Feb 20, 2026

Codecov Report

❌ Patch coverage is 90.18036% with 98 lines in your changes missing coverage. Please review.
✅ Project coverage is 89.75%. Comparing base (9a237cd) to head (ae2b9e3).
⚠️ Report is 46 commits behind head on main.

Files with missing lines Patch % Lines
lib/internal/otel/instrumentations.js 78.75% 50 Missing and 1 partial ⚠️
lib/internal/otel/flush.js 88.48% 34 Missing and 1 partial ⚠️
lib/internal/otel/span.js 93.71% 10 Missing and 2 partials ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main   #61907      +/-   ##
==========================================
+ Coverage   89.73%   89.75%   +0.01%     
==========================================
  Files         675      680       +5     
  Lines      204674   205883    +1209     
  Branches    39330    39565     +235     
==========================================
+ Hits       183667   184781    +1114     
- Misses      13283    13387     +104     
+ Partials     7724     7715       -9     
Files with missing lines Coverage Δ
lib/internal/bootstrap/realm.js 96.21% <100.00%> (+0.21%) ⬆️
lib/internal/otel/core.js 100.00% <100.00%> (ø)
lib/internal/otel/id.js 100.00% <100.00%> (ø)
lib/internal/process/pre_execution.js 97.63% <100.00%> (+1.66%) ⬆️
lib/otel.js 100.00% <100.00%> (ø)
src/node_options.cc 76.30% <100.00%> (+0.02%) ⬆️
src/node_options.h 97.90% <100.00%> (+0.01%) ⬆️
lib/internal/otel/span.js 93.71% <93.71%> (ø)
lib/internal/otel/flush.js 88.48% <88.48%> (ø)
lib/internal/otel/instrumentations.js 78.75% <78.75%> (ø)

... and 56 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

urlStr + '/v1/traces';
}

const parsed = new URL(urlStr);
Copy link

@SerenModz21 SerenModz21 Feb 21, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could this condition be simplified to the following?:

const parsed = new URL('/v1/traces', endpoint);

Tests with the Node REPL:

> new URL("/v1/traces", "http://localhost:4318/hello?foo=bar").href
'http://localhost:4318/v1/traces'
> new URL("/v1/traces", "http://localhost:4318/").href
'http://localhost:4318/v1/traces'
> new URL("/v1/traces", "http://localhost:4318").href
'http://localhost:4318/v1/traces'
> new URL("/v1/traces", "http://localhost:4318/v1").href
'http://localhost:4318/v1/traces'
> new URL("/v1/traces", "http://localhost:4318/v1/traces").href
'http://localhost:4318/v1/traces'
> new URL("/v1/traces", "http://localhost:4318/v1/traces/").href
'http://localhost:4318/v1/traces'

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

lib / src Issues and PRs related to general changes in the lib or src directory. needs-ci PRs that need a full CI run.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants