Skip to content

docs(bun): Add ESM/CJS and Bun-specific OTel docs #14594

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Aug 13, 2025
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
127 changes: 19 additions & 108 deletions docs/platforms/javascript/common/opentelemetry/custom-setup.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ supported:
- javascript.node
- javascript.aws-lambda
- javascript.azure-functions
- javascript.bun
- javascript.connect
- javascript.express
- javascript.fastify
Expand Down Expand Up @@ -65,49 +66,7 @@ In order for the Sentry SDK to work as expected, and for it to be in sync with O

The following code snippet shows how to set up Sentry for error monitoring only:

```javascript
const { NodeTracerProvider } = require("@opentelemetry/sdk-trace-node");
const Sentry = require("@sentry/node");
const { SentryPropagator, SentrySampler } = require("@sentry/opentelemetry");
const { registerInstrumentations } = require("@opentelemetry/instrumentation");

const sentryClient = Sentry.init({
dsn: "___DSN___",
skipOpenTelemetrySetup: true,

// Important: We do not define a tracesSampleRate here at all!
// This leads to tracing being disabled

// Disable emitting of spans in the httpIntegration
integrations: [Sentry.httpIntegration({ spans: false })],
});

// Create and configure e.g. NodeTracerProvider
const provider = new NodeTracerProvider({
// This ensures trace propagation works as expected
sampler: sentryClient ? new SentrySampler(sentryClient) : undefined,
});

provider.addSpanProcessor(
new BatchSpanProcessor(
new OTLPTraceExporter({
url: "http://OTLP-ENDPOINT.com/api",
})
)
);

// Initialize the provider
provider.register({
propagator: new SentryPropagator(),
contextManager: new Sentry.SentryContextManager(),
});

registerInstrumentations({
instrumentations: [
// Add OTEL instrumentation here
],
});
```
<PlatformContent includePath="performance/opentelemetry-setup/error-monitoring-only" />

## Required Instrumentation

Expand Down Expand Up @@ -145,13 +104,27 @@ _Available since SDK version 8.35.0_

You can add your own `@opentelemetry/instrumentation-http` instance in your OpenTelemetry setup. However, in this case, you need to disable span creation in Sentry's `httpIntegration`:

```javascript
const sentryClient = Sentry.init({
<PlatformSection notSupported={["javascript.bun"]}>
```javascript
const sentryClient = Sentry.init({
dsn: "___DSN___",
skipOpenTelemetrySetup: true,
integrations: [Sentry.httpIntegration({ spans: false })],
});
```
</PlatformSection>

<PlatformSection supported={["javascript.bun"]}>
```javascript
const sentryClient = Sentry.init({
dsn: "___DSN___",
skipOpenTelemetrySetup: true,
integrations: (integrations) =>
// Also filter out the BunServer integration to avoid emitting duplicated spans from Sentry AND your custom OTel instrumentation
integrations.filter((i) => i.name !== "BunServer")
});
```
</PlatformSection>

It's important that `httpIntegration` is still registered this way to ensure that the Sentry SDK can correctly isolate requests, for example when capturing errors.

Expand All @@ -163,69 +136,7 @@ If tracing is disabled, the Node Fetch instrumentation will not emit any spans.

While you can use your own sampler, we recommend that you use the `SentrySampler`. This will ensure that the correct subset of traces will be sent to Sentry, based on your `tracesSampleRate`. It will also ensure that all other Sentry features like trace propagation work as expected. If you do need to use your own sampler, make sure to wrap your `SamplingResult` with our `wrapSamplingDecision` method like in the example below:

```javascript
const { NodeTracerProvider } = require("@opentelemetry/sdk-trace-node");
const Sentry = require("@sentry/node");
const {
SentrySpanProcessor,
SentryPropagator,
SentrySampler,
wrapSamplingDecision,
} = require("@sentry/opentelemetry");

// implements Sampler from "@opentelemetry/sdk-trace-node"
class CustomSampler {
shouldSample(context, _traceId, _spanName, _spanKind, attributes, _links) {
const decision = yourDecisionLogic();

// wrap the result
return wrapSamplingDecision({
decision,
context,
spanAttributes: attributes,
});
}

toString() {
return CustomSampler.name;
}
}

const sentryClient = Sentry.init({
dsn: "___DSN___",
skipOpenTelemetrySetup: true,

// By defining any sample rate,
// tracing intergations will be added by default
// omit this if you do not want any performance integrations to be added
tracesSampleRate: 0,
});

const provider = new NodeTracerProvider({
sampler: new CustomSampler(),
});

// ...rest of your setup

// Validate that the setup is correct
Sentry.validateOpenTelemetrySetup();
```

## ESM Loaders

If your application is running in ESM (`import`/`export` syntax), OpenTelemetry requires you to set up _ESM loader hooks_.

The Sentry SDK will automatically register ESM loader hooks by default.
However, if you have your own OpenTelemetry setup, it is recommended to configure the Sentry SDK to not register these hooks and instead register them yourself.
You can do so by setting `registerEsmLoaderHooks` to `false` and [setting up ESM loader hooks](https://github.com/open-telemetry/opentelemetry-js/blob/main/doc/esm-support.md#instrumentation-hook-required-for-esm):

```javascript
Sentry.init({
dsn: "___DSN___",
skipOpenTelemetrySetup: true,
registerEsmLoaderHooks: false,
});
```
<PlatformContent includePath="performance/opentelemetry-setup/with-custom-sampler" />

<Alert title="Why is it recommended to register loader hooks yourself?">

Expand Down
1 change: 1 addition & 0 deletions docs/platforms/javascript/common/opentelemetry/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ supported:
- javascript.node
- javascript.aws-lambda
- javascript.azure-functions
- javascript.bun
- javascript.connect
- javascript.express
- javascript.fastify
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ supported:
- javascript.node
- javascript.aws-lambda
- javascript.azure-functions
- javascript.bun
- javascript.connect
- javascript.express
- javascript.fastify
Expand All @@ -32,7 +33,23 @@ Sentry supports OpenTelemetry APIs out of the box. Any spans started using OpenT

While the Sentry SDK includes some OpenTelemetry instrumentation out of the box, you may want to add additional instrumentation to your application. This can be done by registering the instrumentation through OpenTelemetry like the example below:

```javascript {12-13}
```javascript {tabTitle: ESM} {12-13}
import * as Sentry from "@sentry/node";
import {
GenericPoolInstrumentation,
} from "@opentelemetry/instrumentation-generic-pool";

Sentry.init({
dsn: "___DSN___",

// The SentrySampler will use this to determine which traces to sample
tracesSampleRate: 1.0,

// Add additional OpenTelemetry instrumentation:
openTelemetryInstrumentations: [new GenericPoolInstrumentation()],
});
```
```javascript {tabTitle: CJS} {12-13}
const Sentry = require("@sentry/node");
const {
GenericPoolInstrumentation,
Expand Down Expand Up @@ -65,7 +82,16 @@ We recommend using `Sentry.startSpan()` and related APIs to create spans, but yo

You can access the tracer Sentry uses via `client.tracer` and then create spans with OpenTelemetry APIs, as shown below:

```javascript
```javascript {tabTitle: ESM}
import * as Sentry from "@sentry/node";

const tracer = Sentry.getClient()?.tracer;
// Now you can use native APIs on the tracer:
tracer.startActiveSpan("span name", () => {
// measure something
});
```
```javascript {tabTitle: CJS}
const Sentry = require("@sentry/node");

const tracer = Sentry.getClient()?.tracer;
Expand All @@ -81,7 +107,12 @@ You can also use any other tracer. All OpenTelemetry spans will be picked up by

You can access the tracer provider set up by Sentry when using Sentry's default OpenTelemetry instrumentation.

```javascript
```javascript {tabTitle: ESM}
import * as Sentry from "@sentry/node";

const provider = Sentry.getClient()?.traceProvider;
```
```javascript {tabTitle: CJS}
const Sentry = require("@sentry/node");

const provider = Sentry.getClient()?.traceProvider;
Expand All @@ -91,7 +122,20 @@ const provider = Sentry.getClient()?.traceProvider;

You can add additional span processors to the tracer provider set up by Sentry when using Sentry's default OpenTelemetry instrumentation.

```javascript
```javascript {tabTitle: ESM}
import * as Sentry from "@sentry/node";

Sentry.init({
dsn: "___DSN___",

// The SentrySampler will use this to determine which traces to sample
tracesSampleRate: 1.0,

// Add additional OpenTelemetry SpanProcessors:
openTelemetrySpanProcessors: [new MySpanProcessor()],
});
```
```javascript {tabTitle: CJS}
const Sentry = require("@sentry/node");

Sentry.init({
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
```javascript
import * as Sentry from "@sentry/bun";
import { SentryContextManager } from "@sentry/node-core";
import { SentryPropagator, SentrySampler } from "@sentry/opentelemetry";

import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
import { OTLPTraceExporter } from "@opentelemetry/exporter-otlp-http";
import { registerInstrumentations } from "@opentelemetry/instrumentation";

const sentryClient = Sentry.init({
dsn: "___DSN___",
// Skipping the OpenTelemetry setup automatically disables emitting spans in the httpIntegration with `spans: false`
skipOpenTelemetrySetup: true,

// Important: We do not define a tracesSampleRate here at all!
// This leads to tracing being disabled

integrations: (integrations) =>
// Filter out the BunServer integration to avoid emitting spans from there
integrations.filter((i) => i.name !== "BunServer")
});

// Create and configure e.g. NodeTracerProvider
const provider = new NodeTracerProvider({
// This ensures trace propagation works as expected
sampler: sentryClient ? new SentrySampler(sentryClient) : undefined,
});

provider.addSpanProcessor(
new BatchSpanProcessor(
new OTLPTraceExporter({
url: "http://OTLP-ENDPOINT.com/api",
})
)
);

// Initialize the provider
provider.register({
propagator: new SentryPropagator(),
contextManager: new SentryContextManager(),
});

registerInstrumentations({
instrumentations: [
// Add OTEL instrumentation here
],
});
```
Copy link
Member Author

Choose a reason for hiding this comment

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

The javascript.mdx files are just copied from the general docs page.

I only added some missing imports (BatchSpanProcessor and OTLPTraceExporter) and added the ESM version of it.

Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
```javascript {tabTitle: ESM}
import * as Sentry from "@sentry/node";
import { SentryPropagator, SentrySampler } from "@sentry/opentelemetry";

import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
import { BatchSpanProcessor } from "@opentelemetry/sdk-trace-base";
import { OTLPTraceExporter } from "@opentelemetry/exporter-otlp-http";
import { registerInstrumentations } from "@opentelemetry/instrumentation";

const sentryClient = Sentry.init({
dsn: "___DSN___",
// Skipping the OpenTelemetry setup automatically disables emitting spans in the httpIntegration with `spans: false`
skipOpenTelemetrySetup: true,

// Important: We do not define a tracesSampleRate here at all!
// This leads to tracing being disabled
});

// Create and configure e.g. NodeTracerProvider
const provider = new NodeTracerProvider({
// This ensures trace propagation works as expected
sampler: sentryClient ? new SentrySampler(sentryClient) : undefined,
});

provider.addSpanProcessor(
new BatchSpanProcessor(
new OTLPTraceExporter({
url: "http://OTLP-ENDPOINT.com/api",
})
)
);

// Initialize the provider
provider.register({
propagator: new SentryPropagator(),
contextManager: new Sentry.SentryContextManager(),
});

registerInstrumentations({
instrumentations: [
// Add OTEL instrumentation here
],
});
```
```javascript {tabTitle: CJS}
const Sentry = require("@sentry/node");
const { SentryPropagator, SentrySampler } = require("@sentry/opentelemetry");

const { NodeTracerProvider } = require("@opentelemetry/sdk-trace-node");
const { BatchSpanProcessor } = require("@opentelemetry/sdk-trace-base");
const { OTLPTraceExporter } = require("@opentelemetry/exporter-otlp-http");
const { registerInstrumentations } = require("@opentelemetry/instrumentation");

const sentryClient = Sentry.init({
dsn: "___DSN___",
// Skipping the OpenTelemetry setup automatically disables emitting spans in the httpIntegration with `spans: false`
skipOpenTelemetrySetup: true,

// Important: We do not define a tracesSampleRate here at all!
// This leads to tracing being disabled
});

// Create and configure e.g. NodeTracerProvider
const provider = new NodeTracerProvider({
// This ensures trace propagation works as expected
sampler: sentryClient ? new SentrySampler(sentryClient) : undefined,
});

provider.addSpanProcessor(
new BatchSpanProcessor(
new OTLPTraceExporter({
url: "http://OTLP-ENDPOINT.com/api",
})
)
);

// Initialize the provider
provider.register({
propagator: new SentryPropagator(),
contextManager: new Sentry.SentryContextManager(),
});

registerInstrumentations({
instrumentations: [
// Add OTEL instrumentation here
],
});
```
Loading
Loading