In late February 2025, the OpenTelemetry JavaScript project released the first versions of "JS SDK 2.x" packages, which include a number of breaking changes. This document shows how to migrate to the new 2.x. For the most part, this document only covers breaking changes. Refer to the full changelogs for these releases here:
Per OpenTelemetry guidelines, the 1.x versions of stable SDK packages will be supported for one year from the 2.0.0 release.
If you have any questions about the 2.x changes, please ask! You can reach the OTel JS community on the #otel-js channel of the CNCF Slack, open a Discussion issue on the repository, or join the weekly OTel JS SIG zoom call.
"JS SDK 2.x" encompasses new releases of the @opentelemetry/*
JavaScript packages published from the opentelemetry-js.git repository, except the API and semantic-conventions packages β categories 3 and 4 in the groupings below. The package versions for this new major will be >=2.0.0
for the stable and >=0.200.0
for the unstable packages. (The jump to 0.200.x
was intentional, to hopefully help signal that these packages are in the "2.x generation".)
If your usage of OpenTelemetry does not directly use these packages, then you most likely will not need to change your code to migrate. You still need to be aware of the new minimum versions of Node.js, TypeScript, and ES that are supported.
Categories of OpenTelemetry JS packages
The OpenTelemetry JS SIG is responsible for numerous packages, all published to npm under the @opentelemetry/
org, and developed in two git repositories: opentelemetry-js.git (sometimes called the "core" repo) and opentelemetry-js-contrib.git (the "contrib" repo). For the sake of this document, these packages can be grouped into these categories:
- The API (
@opentelemetry/api
). The API is versioned independently of all other packages. Its version is not being changed as part of "JS SDK 2.x". - The Semantic Conventions (
@opentelemetry/semantic-conventions
). This package follows the versioning of the language-independent Semantic Conventions. It is not being changed as part of "JS SDK 2.x". - Stable packages from opentelemetry-js.git. These are packages that have reached "1.x". These packages are all versioned together, and are at version "1.30.0" at the time of writing.
- Unstable packages from opentelemetry-js.git. These are packages deemed still experimental. They are all at "0.x" versions. These packages are all versioned together, and are at version "0.57.0" at the time of writing.
- All packages from opentelemetry-js-contrib.git. These packages are all versioned independently. These packages are not considered part of the "JS SDK 2.0" release. However, eventually they will update to use JS SDK 2.x releases.
"JS SDK 2.x" refers to categories 3 and 4.
The full set of "JS SDK 2.x" packages
Package | Version |
---|---|
@opentelemetry/context-async-hooks | 2.0.0 |
@opentelemetry/context-zone | 2.0.0 |
@opentelemetry/context-zone-peer-dep | 2.0.0 |
@opentelemetry/core | 2.0.0 |
@opentelemetry/exporter-jaeger | 2.0.0 |
@opentelemetry/exporter-zipkin | 2.0.0 |
@opentelemetry/propagator-b3 | 2.0.0 |
@opentelemetry/propagator-jaeger | 2.0.0 |
@opentelemetry/resources | 2.0.0 |
@opentelemetry/sdk-metrics | 2.0.0 |
@opentelemetry/sdk-trace-base | 2.0.0 |
@opentelemetry/sdk-trace-node | 2.0.0 |
@opentelemetry/sdk-trace-web | 2.0.0 |
@opentelemetry/shim-opentracing | 2.0.0 |
@opentelemetry/api-events | 0.200.0 |
@opentelemetry/api-logs | 0.200.0 |
@opentelemetry/exporter-logs-otlp-grpc | 0.200.0 |
@opentelemetry/exporter-logs-otlp-http | 0.200.0 |
@opentelemetry/exporter-logs-otlp-proto | 0.200.0 |
@opentelemetry/exporter-metrics-otlp-grpc | 0.200.0 |
@opentelemetry/exporter-metrics-otlp-http | 0.200.0 |
@opentelemetry/exporter-metrics-otlp-proto | 0.200.0 |
@opentelemetry/exporter-prometheus | 0.200.0 |
@opentelemetry/exporter-trace-otlp-grpc | 0.200.0 |
@opentelemetry/exporter-trace-otlp-http | 0.200.0 |
@opentelemetry/exporter-trace-otlp-proto | 0.200.0 |
@opentelemetry/instrumentation | 0.200.0 |
@opentelemetry/instrumentation-fetch | 0.200.0 |
@opentelemetry/instrumentation-grpc | 0.200.0 |
@opentelemetry/instrumentation-http | 0.200.0 |
@opentelemetry/instrumentation-xml-http-request | 0.200.0 |
@opentelemetry/opentelemetry-browser-detector | 0.200.0 |
@opentelemetry/otlp-exporter-base | 0.200.0 |
@opentelemetry/otlp-grpc-exporter-base | 0.200.0 |
@opentelemetry/otlp-transformer | 0.200.0 |
@opentelemetry/sampler-jaeger-remote | 0.200.0 |
@opentelemetry/sdk-events | 0.200.0 |
@opentelemetry/sdk-logs | 0.200.0 |
@opentelemetry/sdk-node | 0.200.0 |
@opentelemetry/shim-opencensus | 0.200.0 |
@opentelemetry/web-common | 0.200.0 |
The minimum supported Node.js has been raised to ^18.19.0 || >=20.6.0
. This means that support for Node.js 14 and 16 has been dropped.
Note
- The minimum supported Node.js versions for
@opentelemetry/api
(Node.js v8) and@opentelemetry/semantic-conventions
(Node.js v14) are not changing as part of "JS SDK 2.x". - The particular minimum minor versions of Node.js 18 and 20 were selected to include support for Node.js's
--import
flag andmodule.register()
API. It is expected that this will provide a smoother experience for improved automatic ES module instrumentation.
Related issues and PRs: #5395
The minimum supported TypeScript version has been raised to 5.0.4.
As well, going forward all packages published from this repository will drop support for old versions of typescript
in minor releases. We will only drop support for versions that are older than 2 years.
Important: Both of these changes ([email protected], dropping old TypeScript versions in minor releases) also apply to the @opentelemetry/api
and @opentelemetry/semantic-conventions
packages, even though those two packages aren't otherwise considered part of the "JS SDK 2.x" update.
Note
Related issues and PRs: #5145
The compilation target for transpiled TypeScript has been raised to ES2022 (from ES2017) for all packages (except @opentelemetry/api
, @opentelemetry/api-logs
, @opentelemetry/api-events
, and @opentelemetry/semantic-conventions
).
For Browser usage, this drops support for any browser versions that do not support ES2022 features. For Node.js usage, this already follows from the new minimum supported Node.js versions mentioned above.
For browser users, support for window.OTEL_*
environment variable configuration (previous handled by the envDetector
) has been dropped. OpenTelemetry bootstrap code for the browser should be configured via code.
Perhaps the most likely API change you will need to update for is from the @opentelemetry/resources
package.
The Resource
class is no longer exported; instead use new utility functions.
- Creating a resource:
new Resource(...)
->resourceFromAttributes(...)
- Getting the default resource:
Resource.default()
->defaultResource()
- Getting an empty resource:
Resource.empty()
orResource.EMPTY
->emptyResource()
- TypeScript interface for a resource:
IResource
->Resource
// Before
import { Resource } from '@opentelemetry/resources';
new Resource({ ... }); // Create a resource
Resource.default(); // Get a resource with the default attributes
Resource.empty(); // Get an empty resource
// After
import { resourceFromAttributes, defaultResource, emptyResource } from '@opentelemetry/resources';
resourceFromAttributes({ ... });
defaultResource();
emptyResource();
"Sync" and async resource detectors have been unified. For example, where before there were both hostDetector
and hostDetectorSync
, now there is only hostDetector
which may be used in all cases.
envDetectorSync
->envDetector
hostDetectorSync
->hostDetector
osDetectorSync
->osDetector
processDetectorSync
->processDetector
serviceInstanceIdDetectorSync
->serviceInstanceIdDetector
detectResourcesSync
->detectResources
The browserDetector
and browserDetectorSync
exports were dropped. This resource detector was long ago replaced by the (semantic-conventions-compliant) browser detector in the separate @opentelemetry/opentelemetry-browser-detector
package.
browserDetector
orbrowserDetectorSync
->import { browserDetector } from '@opentelemetry/opentelemetry-browser-detector'
As mentioned above, support for window.OTEL_*
environment variable configuration in browsers has been dropped. This means that the envDetector
in browsers is now a no-op.
envDetector
in a browser -> manually create a resource with the API
In TypeScript code, the ResourceAttributes
type was replaced with the Attributes
type from the 'api' package. Though unlikely, it is possible this could be a breaking change because it raised the minimum peerDependencies
entry for @opentelemetry/api
from 1.0.0
to 1.3.0
.
- type
ResourceAttributes
->import type { Attributes } from '@opentelemetry/api';
If you maintain an implementation of a resource detector, i.e. if you have a class that implements DetectorSync
(or the deprecated Detector
) interface from @opentelemetry/resources
, then please see the section below for implementors of resource detectors.
class ... implements DetectorSync
-> see section below for implementation changes
Note
- In general, the OTel JS packages are trending away from exporting classes because that results in exporting types with internal details that inhibit later refactoring. See #5283 for details.
- The unification of sync and async resource detectors simplified the API, clarified the behavior for merging results from multiple detectors, and laid the groundwork for supporting OpenTelemetry Entities in the future. See #5350 for details.
The environment variable utilities have changed to no longer have one large load and parse of all possible OTEL_*
environment variables. Instead there are get{Type}FromEnv()
utilities to handle the various specified OpenTelemetry SDK environment variable types.
The caller should now handle default values. The authority for default values is the OpenTelemetry Environment Variable Spec. The previously used defaults in the 1.x code can be seen here.
getEnv().OTEL_FOO
->get{Type}FromEnv('OTEL_FOO') ?? defaultValue
getStringFromEnv()
getNumberFromEnv()
getBooleanFromEnv()
getStringListFromEnv()
diagLogLevelFromString()
for readingOTEL_LOG_LEVEL
getEnvWithoutDefaults().OTEL_FOO
->get{Type}FromEnv('OTEL_FOO')
- The following have been removed without replacement:
DEFAULT_ENVIRONMENT
ENVIRONMENT
RAW_ENVIRONMENT
parseEnvironment
For example:
// Before
import { getEnv, getEnvWithoutDefaults } from '@opentelemetry/core';
const flavor = getEnv().OTEL_EXPORTER_OTLP_PROTOCOL;
const limit = getEnv().OTEL_BSP_MAX_QUEUE_SIZE;
const level = getEnv().OTEL_LOG_LEVEL;
// After
import { getStringFromEnv, getNumberFromEnv, diagLogLevelFromString } from '@opentelemetry/core';
const flavor = getStringFromEnv('OTEL_EXPORTER_OTLP_PROTOCOL') ?? 'http/protobuf';
const limit = getNumberFromEnv('OTEL_BSP_MAX_QUEUE_SIZE') ?? 2048;
const level = diagLogLevelFromString(getStringFromEnv('OTEL_LOG_LEVEL'));
A number of deprecated, obsolete, unused, and accidentally exported functions and variables have been removed from the core
package. (If there is a replacement, it is mentioned with -> ...
.)
IdGenerator
andRandomIdGenerator
(deprecated)- type
InstrumentationLibrary
(deprecated) -> typeInstrumentationScope
AlwaysOnSampler
(deprecated) -> moved to@opentelemetry/sdk-trace-base
AlwaysOffSampler
(deprecated) -> moved to@opentelemetry/sdk-trace-base
ParentBasedSampler
(deprecated) -> moved to@opentelemetry/sdk-trace-base
TraceIdRatioSampler
(deprecated) -> moved to@opentelemetry/sdk-trace-base
TracesSamplerValues
(was only used internally)VERSION
isWrapped
-> useisWrapped
from@opentelemetry/instrumentation
ShimWrapped
-> useShimWrapped
from@opentelemetry/instrumentation
hexToBase64
hexToBinary
baggageUtils.getKeyPairs
(unintentionally exported)baggageUtils.serializeKeyPairs
(unintentionally exported)baggageUtils.parseKeyPairsIntoRecord
->parseKeyPairsIntoRecord
baggageUtils.parsePairKeyValue
(unintentionally exported)TimeOriginLegacy
isAttributeKey
(unintentionally exported)DEFAULT_ATTRIBUTE_VALUE_LENGTH_LIMIT
-> useInfinity
DEFAULT_ATTRIBUTE_VALUE_COUNT_LIMIT
-> use128
DEFAULT_SPAN_ATTRIBUTE_PER_EVENT_COUNT_LIMIT
-> use128
DEFAULT_SPAN_ATTRIBUTE_PER_LINK_COUNT_LIMIT
-> use128
Note
- The
getEnv()
et al API changes avoid a problem of requiring an update to@opentelemetry/core
for any addedOTEL_*
envvars, including in unstable packages and packages maintained in the separate contrib repository. See #5443.
Related issues and PRs: #5443 #5481 #5475 #5309 #5308 #5316 #5406 #5444 #5504
This section describes API changes in the set of packages that implement the tracing SDK: @opentelemetry/sdk-trace-base
, @opentelemetry/sdk-trace-node
, @opentelemetry/sdk-trace-web
.
Tracing was the first signal to have an SDK. Over time, and as the Metrics and Logs SDKs were added, the API design separating functionality between the {Tracer,Meter,Logs}Provider
s APIs and the higher level NodeSDK
(in @opentelemetry/sdk-node
) improved. The Tracing SDK was left with some cruft that is being removed in JS SDK 2.0. (See #5290 for motivation.)
- removed
BasicTracerProvider#addSpanProcessor(...)
-> use constructor options to the TracerProvider class - made
BasicTracerProvider#getActiveSpanProcessor()
private - made
BasicTracerProvider#resource
private BasicTracerProvider
andNodeTracerProvider
will no longer use theOTEL_TRACES_EXPORTER
envvar to create exporters -> This functionality already resides inNodeSDK
(from@opentelemetry/sdk-node
).BasicTracerProvider
andNodeTracerProvider
will no longer use theOTEL_PROPAGATORS
envvar to create propagators -> This functionality already resides inNodeSDK
(from@opentelemetry/sdk-node
).- The following internal properties were removed from
BasicTracerProvider
:_registeredExporters
,_getSpanExporter
,_buildExporterFromEnv
- The following exports were dropped from
@opentelemetry/sdk-trace-*
:EXPORTER_FACTORY
is not used anymore and has been removedPROPAGATOR_FACTORY
is not used anymore and has been removedForceFlushState
was intended for internal use and has been removed- the
Tracer
class was unintentionally exported and has been removed- to obtain a
Tracer
, please useBasicTracerProvider#getTracer()
,NodeTracerProvider#getTracer()
orWebTracerProvider#getTracer()
- to reference the
Tracer
type, please use theTracer
type from@opentelemetry/api
- to obtain a
- removed
BasicTracerProvider#register()
-> useNodeTracerProvider#register()
orWebTracerProvider#register()
, or calltrace.setGlobalTracerProvider()
et al manually (see #5503)
The export of the Span
class has been removed. It was not intended to be used directly. One should always use methods on a Tracer
instance (e.g. startSpan()
) for creating spans.
new Span(...)
-> usetracer.startSpan(...)
The parentSpanId
field on the Span
and ReadableSpan
interfaces was replaced by parentSpanContext
, to adhere to the OTel spec. #5450
span.parentSpanId
->span.parentSpanContext?.spanId
As mentioned above in the "core" section, InstrumentationLibrary
has been changed to InstrumentationScope
:
ReadableSpan.instrumentationLibrary
->ReadableSpan.instrumentationScope
When invalid data is set on OTEL_TRACES_SAMPLER
, the SDK now uses ParentBasedAlwaysOnSampler
rather than AlwaysOnSampler
, per spec.
The Metrics SDK now internally uses the Gauge
and MetricAdvice
types from the API package, which requires bumping its peer dependency.
- bumped minimum version of
@opentelemetry/api
peer dependency to 1.9.0
The View
class has been removed in favor of passing an object of type ViewOptions
to a MeterProvider. As well, the *Aggregation
classes have been removed in favor of type AggregationOption
and the AggregationType
enum. (See #4931 for motivation.)
- removed class
View
-> pass atype ViewOptions
object to a MeterProvider - removed
Aggregation
-> pass atype ViewOptions
object to a MeterProvider- removed
DefaultAggregation
-> pass a ViewOptions object withtype: AggregationType.DEFAULT
- removed
DropAggregation
-> pass a ViewOptions object withtype: AggregationType.DROP
- removed
ExponentialHistogramAggregation
-> pass a ViewOptions object withtype: AggregationType.EXPONENTIAL_HISTOGRAM
- removed
ExplicitBucketHistogramAggregation
-> pass a ViewOptions object withtype: AggregationType.EXPLICIT_BUCKET_HISTOGRAM
- removed
HistogramAggregation
-> pass a ViewOptions object withtype: AggregationType.EXPLICIT_BUCKET_HISTOGRAM
- removed
LastValueAggregation
-> pass a ViewOptions object withtype: AggregationType.LAST_VALUE
- removed
SumAggregation
-> pass a ViewOptions object withtype: AggregationType.SUM
- removed
For example:
// Before
import {
MeterProvider,
View,
InstrumentType,
ExplicitBucketHistogramAggregation
} from '@opentelemetry/sdk-metrics';
const provider = new MeterProvider({
views: [
new View({
instrumentName: 'http.server.duration',
instrumentType: InstrumentType.HISTOGRAM,
aggregation: new ExplicitBucketHistogramAggregation([0, 1, 5, 10, 15, 20, 25, 30]),
})
]
});
// After
import {MeterProvider, InstrumentType, AggregationType} from '@opentelemetry/sdk-metrics';
const provider = new MeterProvider({
views: [
{ // type ViewOptions
instrumentName: 'http.server.duration',
instrumentType: InstrumentType.HISTOGRAM,
aggregation: { // type AggregationOption
type: AggregationType.EXPLICIT_BUCKET_HISTOGRAM,
options: {
boundaries: [0, 1, 5, 10, 15, 20, 25, 30],
}
}
}
]
});
The attributeKeys
View option has been replaced with more capable filtering. (See #4532.)
attributeKeys
View
option -> use theattributesProcessors
ViewOptions property, andcreateAllowListAttributesProcessor()
orcreateDenyListAttributesProcessor()
For example:
// Before
import {MeterProvider, View} from '@opentelemetry/sdk-metrics';
const provider = new MeterProvider({
views: [
new View({
attributeKeys: ['attrib1'],
})
]
});
// After
import {MeterProvider, createAllowListAttributesProcessor} from '@opentelemetry/sdk-metrics';
const provider = new MeterProvider({
views: [
{
attributesProcessors: [
createAllowListAttributesProcessor(['attrib1']),
],
}
]
});
Some deprecated things have been removed:
- drop deprecated
type
field on interfaceMetricDescriptor
- drop deprecated
InstrumentDescriptor
type -> useMetricDescriptor
instead
The following changes were made to MetricReader-related APIs:
- removed
MeterProvider.addMetricReader()
-> use thereaders
constructor option - new
IMetricReader
interface -> This is preferred to theMetricReader
abstract class. TheMeterProviderOptions
readers
constructor option now uses this slightly narrower type.- If you accept
MetricReader
in your public interface, prefer accepting the more generalIMetricReader
type instead to avoid unintentional breaking changes.
- If you accept
If you maintain an implementation of a resource detector, then you will need to update for JS SDK 2.x. If you have a class that implements DetectorSync
(or the deprecated Detector
) interface from @opentelemetry/resources
, then this section applies to you. There are two cases: if your detector can gather all attribute data synchronously (this is the easy case), or if your detector needs to asynchronously gather some attribute data.
Before:
import {
DetectorSync,
Resource,
IResource,
} from '@opentelemetry/resources';
class FooDetector implements DetectorSync {
detect(): IResource {
const attributes = {
'foo.bar': process.env.FOO_BAR,
'foo.baz': process.env.FOO_BAZ,
};
return new Resource(attributes);
}
}
export const fooDetector = new FooDetector();
After:
import { ResourceDetector, DetectedResource } from '@opentelemetry/resources';
// 1. `ResourceDetector` is the interface name now
class FooDetector implements ResourceDetector {
detect(): DetectedResource { // 2.
const attributes = {
'foo.bar': process.env.FOO_BAR,
'foo.baz': process.env.FOO_BAZ,
};
// 2. The `.detect()` method now returns a vanilla JS object with the
// attributes, rather than building a `Resource` instance. The
// type is `DetectedResource` rather than `IResource`.
return { attributes };
}
}
export const fooDetector = new FooDetector();
If your resource detector implementation asynchronously gathers attribute data, then the migration to JS SDK 2.x will be a little bit more work. In the newer @opentelemetry/resources
, the ResourceDetector#detect()
method must synchronously return every attribute name that it may provide. Any of those attribute values can be a Promise that resolves to a value or to undefined
if not applicable.
Before:
import {
DetectorSync,
Resource,
IResource,
ResourceAttributes,
} from '@opentelemetry/resources';
class FooDetector implements DetectorSync {
detect(): IResource {
// A common pattern was to asynchronously gather attributes in a separate
// async function and pass that Promise to the second argument to
// `new Resource(...)`.
return new Resource({}, this._getAttributes());
}
private async _getAttributes(): Promise<ResourceAttributes> {
try {
const data = await this._someAsyncFunctionToGatherData();
return {
'foo.pid': data.pid,
'foo.agentUuid': data.agentUuid,
};
} catch {
return {};
}
}
}
export const fooDetector = new FooDetector();
After:
import {
ResourceDetector,
DetectedResource,
DetectedResourceAttributes,
} from '@opentelemetry/resources';
// 1. `ResourceDetector` is the interface name now.
class FooDetector implements ResourceDetector {
// 2. `DetectedResource` is the return type now.
detect(): DetectedResource {
// 3. Get all attributes, as before. Cannot `await` them.
const dataPromise = this._gatherData();
// 4. List all the possible attribute names returned by this detector.
const attrNames = [
'foo.pid',
'foo.agentUuid',
];
const attributes = {} as DetectedResourceAttributes;
attrNames.forEach(name => {
// Each resource attribute is determined asynchronously in _gatherData().
attributes[name] = dataPromise.then(data => data[name]);
});
return { attributes };
}
// 5. Other than the change in function name and return type, this method is
// unchanged from the `_getAttributes` above.
private async _gatherData(): Promise<DetectedResourceAttributes> {
try {
const data = await this._someAsyncFunctionToGatherData();
return {
'foo.pid': data.pid,
'foo.agentUuid': data.agentUuid,
};
} catch {
return {};
}
}
}
export const fooDetector = new FooDetector();
This shows one way that can localize all code changes to the .detect()
method.
A concrete example of this can be found in this commit that migrated the InstanaAgentDetector
in the @opentelemetry/resource-detector-instana
package.
In your tests, you may need to change how to get a Resource
instance for assertions.
Before:
const resource = await fooDetector.detect();
assert.deepStrictEqual(resource.attributes, { ... });
After:
import { detectResources } from '@opentelemetry/resources';
const resource = detectResources({ detectors: [fooDetector] });
await resource.waitForAsyncAttributes?.();
assert.deepStrictEqual(resource.attributes, { ... });
This section describes the remaining breaking changes, not otherwise mentioned in a section above.
Usage of the deprecated SpanAttributes
and MetricAttributes
types from the API package has been changed to use the Attributes
type.
- bumped minimum version of
@opentelemetry/api
peer dependency to 1.1.0 for the following packages:@opentelemetry/core
#4408,@opentelemetry/resources
#4428,@opentelemetry/sdk-trace-base
#5009,@opentelemetry/shim-opentracing
#4430
And finally:
@opentelemetry/sdk-node
: The type ofNodeSDKConfiguration.metricReader
has narrowed slightly fromMetricReader
toIMetricReader
. #5311@opentelemetry/exporter-jaeger
:ReadableSpan.instrumentationLibrary
->ReadableSpan.instrumentationScope
#5308@opentelemetry/exporter-zipkin
:ReadableSpan.instrumentationLibrary
->ReadableSpan.instrumentationScope
#5308@opentelemetry/exporter-prometheus
: Any non-monotonic sums will now be treated as counters and will now include the_total
suffix. #5291 #5266 (comment)@opentelemetry/shim-opencenus
: stop mapping removed Instrumenttype
to OpenTelemetry metrics #5291@opentelemetry/instrumentation-fetch
: Passthrough original response toapplyCustomAttributes
hook, rather than cloning the response. This means it is no longer possibly to consume the response inapplyCustomAttributes
. #5281