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
10 changes: 10 additions & 0 deletions MIGRATION.md
Original file line numberDiff line numberDiff line change
Expand Up@@ -819,6 +819,16 @@ Affected SDKs: All server-side SDKs.

The LangGraph instrumentation no longer emits `gen_ai.create_agent` spans when a graph is compiled. `gen_ai.invoke_agent` and `gen_ai.execute_tool` spans are unaffected. If you reference `create_agent` spans in dashboards or alerts, update them accordingly.

### Express: errors are captured automatically

Affected SDKs: All server-side SDKs that support Express.

`expressIntegration()` now captures errors thrown from your route handlers automatically, so calling `setupExpressErrorHandler(app)` is no longer necessary — the call can be removed. It is deprecated and will be removed in the next major version. To customize which errors are captured, pass `shouldHandleError` to `expressIntegration()` (by default, 5xx errors and errors without a resolvable status are captured, while 3xx/4xx errors are not).

If you prefer to capture errors yourself, set `expressIntegration({ shouldHandleError: false })` to opt out of automatic capture entirely, and call `Sentry.captureException` from your own error-handling middleware.

The `expressErrorHandler` and `patchExpressModule` exports are deprecated for the same reason and will be removed in the next major version. The export of `expressErrorHandler` and `setupExpressErrorHandler` is moved from `@sentry/core` to `@sentry/server-utils`.

### Span name changes

Affected SDKs: All SDKs.
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -42,8 +42,6 @@ app.get('/test-error', function (req, res) {
}, 100);
});

Sentry.setupExpressErrorHandler(app);

app.use(function onError(err, req, res, next) {
// The error id is attached to `res.sentry` to be returned
// and optionally displayed to the user for support.
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -32,8 +32,6 @@ app.get('/test-error', function (req, res) {
}, 100);
});

Sentry.setupExpressErrorHandler(app);

app.use(function onError(err, req, res, next) {
// The error id is attached to `res.sentry` to be returned
// and optionally displayed to the user for support.
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -12,8 +12,6 @@ app.get('/test-success', function (_req, res) {
res.send({ version: 'v1' });
});

Sentry.setupExpressErrorHandler(app);

app.listen(port, () => {
console.log(`Example app listening on port ${port}`);
});
Original file line numberDiff line numberDiff line change
Expand Up@@ -97,8 +97,6 @@ app.get('/test-local-variables-caught', function (req, res) {
res.send({ exceptionId, randomVariableToRecord });
});

Sentry.setupExpressErrorHandler(app);

// @ts-ignore
app.use(function onError(err, req, res, next) {
// The error id is attached to `res.sentry` to be returned
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -21,7 +21,7 @@ test('Sends correct error event', async ({ baseURL }) => {
const exception = errorEvent.exception?.values?.[0];
expect(exception?.value).toBe('This is an exception with id 123');
expect(exception?.mechanism).toEqual({
type: 'auto.middleware.express',
type: 'auto.http.express',
handled: false,
});

Expand All@@ -37,6 +37,7 @@ test('Sends correct error event', async ({ baseURL }) => {
expect(errorEvent.contexts?.trace).toEqual({
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
span_id: expect.stringMatching(/[a-f0-9]{16}/),
parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
});

// The error is attached to the same trace as the streamed request spans, and to a
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -104,8 +104,6 @@ app.get('/test-local-variables-caught', function (req, res) {
res.send({ exceptionId, randomVariableToRecord });
});

Sentry.setupExpressErrorHandler(app);

// @ts-ignore
app.use(function onError(err, req, res, next) {
// The error id is attached to `res.sentry` to be returned
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,6 +30,7 @@ test('Sends correct error event', async ({ baseURL }) => {
expect(errorEvent.contexts?.trace).toEqual({
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
span_id: expect.stringMatching(/[a-f0-9]{16}/),
parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
});

// The error is attached to the same trace as the request transaction, and to a
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -105,8 +105,6 @@ app.get('/test-local-variables-caught', function (req, res) {
res.send({ exceptionId, randomVariableToRecord });
});

Sentry.setupExpressErrorHandler(app);

// @ts-ignore
app.use(function onError(err, req, res, next) {
// The error id is attached to `res.sentry` to be returned
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -19,7 +19,7 @@ test('Sends correct error event', async ({ baseURL }) => {
const exception = errorEvent.exception?.values?.[0];
expect(exception?.value).toBe('This is an exception with id 123');
expect(exception?.mechanism).toEqual({
type: 'auto.middleware.express',
type: 'auto.http.express',
handled: false,
});

Expand All@@ -35,6 +35,7 @@ test('Sends correct error event', async ({ baseURL }) => {
expect(errorEvent.contexts?.trace).toEqual({
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
span_id: expect.stringMatching(/[a-f0-9]{16}/),
parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),
});

// The error is attached to the same trace as the request transaction, and to a
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -74,8 +74,6 @@ app.get('/test-local-variables-caught', function (req, res) {
res.send({ exceptionId, randomVariableToRecord });
});

Sentry.setupExpressErrorHandler(app);

// @ts-ignore
app.use(function onError(err, req, res, next) {
// The error id is attached to `res.sentry` to be returned
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -30,6 +30,7 @@ test('Sends correct error event', async ({ baseURL }) => {
expect(errorEvent.contexts?.trace).toEqual({
trace_id: expect.stringMatching(/[a-f0-9]{32}/),
span_id: expect.stringMatching(/[a-f0-9]{16}/),
parent_span_id: expect.stringMatching(/[a-f0-9]{16}/),

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

q: A lot of parent_span_ids were added in the assertions. Was anything changed that this changed or was added?

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

this is more correct now then before I think, what has changed here is in what span context the error is captured, we ensure to capture this in the span context of the route handler now instead of in the separate middleware which should be better than before.

});

// The error is attached to the same trace as the request transaction, and to a
Expand Down
Original file line numberDiff line numberDiff line change
Expand Up@@ -17,6 +17,4 @@ app.get('/test/success', (req, res) => {
res.send({ response: 'response 3' });
});

Sentry.setupExpressErrorHandler(app);

startExpressServerAndSendPortToRunner(app);
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,6 +15,4 @@ app.get('/test/success', (req, res) => {
res.send({ response: 'response 3' });
});

Sentry.setupExpressErrorHandler(app);

startExpressServerAndSendPortToRunner(app);
Original file line numberDiff line numberDiff line change
Expand Up@@ -6,4 +6,7 @@ Sentry.init({
dsn: 'https://public@dsn.ingest.sentry.io/1337',
release: '1.0',
transport: loggingTransport,
// With tracing off, `expressIntegration()` is not a default integration, so opt in explicitly to
// get automatic error capture.
Comment on lines +9 to +10

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this is no longer true. Express integration is in the "framework" set, so it's on by default, tracing or no.

Copy link
Copy Markdown
MemberAuthor

Choose a reason for hiding this comment

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

👍 can you include in a follow up?

integrations: [Sentry.expressIntegration()],
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
import * as Sentry from '@sentry/node';
import { loggingTransport } from '@sentry-internal/node-integration-tests';

// Disable the channel-based `expressIntegration()` so the deprecated `setupExpressErrorHandler`
// middleware is the sole error capturer (mechanism `auto.middleware.express`) — the fallback for
// setups where the channel-based auto capture is unavailable.
Sentry.init({
traceLifecycle: 'static',
dsn: 'https://public@dsn.ingest.sentry.io/1337',
release: '1.0',
transport: loggingTransport,
integrations: integrations => integrations.filter(integration => integration.name !== 'Express'),
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
import * as Sentry from '@sentry/node';
import { loggingTransport } from '@sentry-internal/node-integration-tests';

Sentry.init({
traceLifecycle: 'static',
dsn: 'https://public@dsn.ingest.sentry.io/1337',
release: '1.0',
transport: loggingTransport,
integrations: [
Sentry.expressIntegration({
shouldHandleError: error => {
return error.message === 'error_2';
},
}),
],
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
import * as Sentry from '@sentry/node';
import { startExpressServerAndSendPortToRunner } from '@sentry-internal/node-integration-tests';
import cors from 'cors';
import express from 'express';

const app = express();

app.use(cors());

app.get('/test1', (_req, _res) => {
throw new Error('error_1');
});

app.get('/test2', (_req, _res) => {
throw new Error('error_2');
});

// With `expressIntegration` disabled (see the instrument file), the deprecated middleware is the sole
// capturer and its own `shouldHandleError` applies.
Sentry.setupExpressErrorHandler(app, {
shouldHandleError: error => error.message === 'error_2',
});

startExpressServerAndSendPortToRunner(app);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
import * as Sentry from '@sentry/node';
import { startExpressServerAndSendPortToRunner } from '@sentry-internal/node-integration-tests';
import cors from 'cors';
import express from 'express';

const app = express();

app.use(cors());

app.get('/test1', (_req, _res) => {
throw new Error('error_1');
});

app.get('/test2', (_req, _res) => {
throw new Error('error_2');
});

// Deprecated, and here with a permissive (default) predicate that would capture both errors. But the
// channel-based `expressIntegration` (configured with `shouldHandleError: error_2` in the instrument)
// takes precedence: it is the single registered handler, so its predicate decides what is captured and
// this middleware must neither capture `error_1` nor double-capture `error_2`.
Sentry.setupExpressErrorHandler(app);

startExpressServerAndSendPortToRunner(app);
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
import * as Sentry from '@sentry/node';
import { startExpressServerAndSendPortToRunner } from '@sentry-internal/node-integration-tests';
import cors from 'cors';
import express from 'express';
Expand All@@ -15,10 +14,7 @@ app.get('/test2', (_req, _res) => {
throw new Error('error_2');
});

Sentry.setupExpressErrorHandler(app, {
shouldHandleError: error => {
return error.message === 'error_2';
},
});
// `shouldHandleError` is configured on `expressIntegration` (see the instrument file); no
// error handler needs to be registered on the app anymore.

startExpressServerAndSendPortToRunner(app);
Original file line numberDiff line numberDiff line change
Expand Up@@ -29,6 +29,4 @@ app.get('/test/withIsolationScope', () => {
});
});

Sentry.setupExpressErrorHandler(app);

startExpressServerAndSendPortToRunner(app);
Loading
Loading