diff --git a/src/platforms/node/common/configuration/async-context.mdx b/src/platforms/node/common/configuration/async-context.mdx new file mode 100644 index 0000000000000..bfe49e6418681 --- /dev/null +++ b/src/platforms/node/common/configuration/async-context.mdx @@ -0,0 +1,33 @@ +--- +title: Async Context +sidebar_order: 80 +description: "Learn more about how to isolate Sentry scope and breadcrumbs across requests." +--- + +You can use the `runWithAsyncContext` method to isolate Sentry scope and breadcrumbs to a single request if you are using SDK v7.48.0 or higher. This is useful if you are finding that breadcrumbs and scope are leaking across requests. + +```js +const Sentry = require("@sentry/node"); + +function requestHandlerMiddleware(req, res, next) { + // Any breadcrumbs or tags added will be isolated to the request + return Sentry.runWithAsyncContext(() => { + return next(req, res); + }); +} +``` + +Under the hood, the SDK uses Node's [AsyncLocalStorage API](https://nodejs.org/api/async_context.html#class-asynclocalstorage) to perform the isolation. + +On lower SDK versions you'll have to use [domains](https://nodejs.org/api/domain.html) to isolate Sentry scope and breadcrumbs to a single request. + +```js +const domain = require("domain"); + +function myRequestHandler(req, res, next) { + const localDomain = domain.create(); + return localDomain.bind(() => { + return next(req, res); + })(); +} +``` diff --git a/src/platforms/node/guides/express/index.mdx b/src/platforms/node/guides/express/index.mdx index d45a8199e97d0..3b571526ef92d 100644 --- a/src/platforms/node/guides/express/index.mdx +++ b/src/platforms/node/guides/express/index.mdx @@ -167,8 +167,8 @@ Sentry.init({ tracesSampleRate: 1.0, }); -// RequestHandler creates a separate execution context using domains, so that every -// transaction/span/breadcrumb is attached to its own Hub instance +// RequestHandler creates a separate execution context, so that all +// transactions/spans/breadcrumbs are isolated across requests app.use(Sentry.Handlers.requestHandler()); // TracingHandler creates a trace for every incoming request app.use(Sentry.Handlers.tracingHandler()); diff --git a/src/platforms/node/guides/express/performance/index.mdx b/src/platforms/node/guides/express/performance/index.mdx index 3684ae755118b..5e8df518abd8a 100644 --- a/src/platforms/node/guides/express/performance/index.mdx +++ b/src/platforms/node/guides/express/performance/index.mdx @@ -35,8 +35,8 @@ Sentry.init({ tracesSampleRate: 1.0, }); -// RequestHandler creates a separate execution context using domains, so that every -// transaction/span/breadcrumb is attached to its own Hub instance +// RequestHandler creates a separate execution context, so that all +// transactions/spans/breadcrumbs are isolated across requests app.use(Sentry.Handlers.requestHandler()); // TracingHandler creates a trace for every incoming request app.use(Sentry.Handlers.tracingHandler()); @@ -132,6 +132,7 @@ app.get("/success", function successHandler(req, res) { ## Connecting Services If you are also using Performance Monitoring for [JavaScript](/platforms/javascript/performance/), depending on where your request originates, you can connect traces: + 1. For requests that start in your backend, by [adding a meta tag](/platforms/javascript/performance/connect-services/#pageload) in your HTML template that contains tracing information. 2. For requests that start in JavaScript, by the SDK [setting a header](/platforms/javascript/performance/connect-services/#navigation-and-other-xhr-requests) on requests to your backend. diff --git a/src/platforms/node/guides/express/performance/instrumentation/automatic-instrumentation.mdx b/src/platforms/node/guides/express/performance/instrumentation/automatic-instrumentation.mdx index 2cdc10c624211..f81c4548b41f7 100644 --- a/src/platforms/node/guides/express/performance/instrumentation/automatic-instrumentation.mdx +++ b/src/platforms/node/guides/express/performance/instrumentation/automatic-instrumentation.mdx @@ -37,8 +37,8 @@ Sentry.init({ tracesSampleRate: 1.0, }); -// RequestHandler creates a separate execution context using domains, so that every -// transaction/span/breadcrumb is attached to its own Hub instance +// RequestHandler creates a separate execution context, so that all +// transactions/spans/breadcrumbs are isolated across requests app.use(Sentry.Handlers.requestHandler()); // TracingHandler creates a trace for every incoming request app.use(Sentry.Handlers.tracingHandler()); diff --git a/src/platforms/node/guides/koa/index.mdx b/src/platforms/node/guides/koa/index.mdx index 091de44ce8902..0a51f3cb1f9fe 100644 --- a/src/platforms/node/guides/koa/index.mdx +++ b/src/platforms/node/guides/koa/index.mdx @@ -49,7 +49,6 @@ const Sentry = require("@sentry/node"); const { stripUrlQueryAndFragment } = require("@sentry/utils"); const Koa = require("koa"); const app = new Koa(); -const domain = require("domain"); Sentry.init({ dsn: "___PUBLIC_DSN___", @@ -64,27 +63,20 @@ Sentry.init({ ], }); -// not mandatory, but adding domains does help a lot with breadcrumbs const requestHandler = (ctx, next) => { return new Promise((resolve, reject) => { - const local = domain.create(); - local.add(ctx); - local.on("error", err => { - ctx.status = err.status || 500; - ctx.body = err.message; - ctx.app.emit("error", err, ctx); - reject(err); - }); - local.run(async () => { - Sentry.getCurrentHub().configureScope(scope => + Sentry.runWithAsyncContext(async () => { + const hub = Sentry.getCurrentHub(); + hub.configureScope(scope => scope.addEventProcessor(event => Sentry.addRequestDataToEvent(event, ctx.request, { include: { user: false, }, - }); - ); + }) + ) ); + await next(); resolve(); }); @@ -99,7 +91,9 @@ const tracingMiddleWare = async (ctx, next) => { // connect to trace of upstream app let traceparentData; if (ctx.request.get("sentry-trace")) { - traceparentData = Sentry.extractTraceparentData(ctx.request.get("sentry-trace")); + traceparentData = Sentry.extractTraceparentData( + ctx.request.get("sentry-trace") + ); } const transaction = Sentry.startTransaction({ @@ -136,7 +130,7 @@ app.use(tracingMiddleWare); // usual error handler app.on("error", (err, ctx) => { - Sentry.withScope((scope) => { + Sentry.withScope(scope => { scope.addEventProcessor(event => { return Sentry.addRequestDataToEvent(event, ctx.request); }); diff --git a/src/platforms/node/guides/serverless-cloud/index.mdx b/src/platforms/node/guides/serverless-cloud/index.mdx index 5222464aac73a..98d81edb37aca 100644 --- a/src/platforms/node/guides/serverless-cloud/index.mdx +++ b/src/platforms/node/guides/serverless-cloud/index.mdx @@ -142,8 +142,8 @@ Sentry.init({ // tracesSampleRate: parseFloat(params.SENTRY_TRACES_SAMPLE_RATE), }); -// RequestHandler creates a separate execution context using domains, so that every -// transaction/span/breadcrumb is attached to its own Hub instance +// RequestHandler creates a separate execution context, so that all +// transactions/spans/breadcrumbs are isolated across requests api.use(Sentry.Handlers.requestHandler()); // TracingHandler creates a trace for every incoming request api.use(Sentry.Handlers.tracingHandler()); diff --git a/src/wizard/node/express.md b/src/wizard/node/express.md index c542b17ac1f71..f29d648595b96 100644 --- a/src/wizard/node/express.md +++ b/src/wizard/node/express.md @@ -44,8 +44,8 @@ Sentry.init({ tracesSampleRate: 1.0, }); -// RequestHandler creates a separate execution context using domains, so that every -// transaction/span/breadcrumb is attached to its own Hub instance +// RequestHandler creates a separate execution context, so that all +// transactions/spans/breadcrumbs are isolated across requests app.use(Sentry.Handlers.requestHandler()); // TracingHandler creates a trace for every incoming request app.use(Sentry.Handlers.tracingHandler()); diff --git a/src/wizard/node/serverlesscloud.md b/src/wizard/node/serverlesscloud.md index 6cc2c304a3fa1..23f19d464fdb1 100644 --- a/src/wizard/node/serverlesscloud.md +++ b/src/wizard/node/serverlesscloud.md @@ -43,8 +43,8 @@ Sentry.init({ // tracesSampleRate: parseFloat(params.SENTRY_TRACES_SAMPLE_RATE), }); -// RequestHandler creates a separate execution context using domains, so that every -// transaction/span/breadcrumb is attached to its own Hub instance +// RequestHandler creates a separate execution context, so that all +// transactions/spans/breadcrumbs are isolated across requests api.use(Sentry.Handlers.requestHandler()); // TracingHandler creates a trace for every incoming request api.use(Sentry.Handlers.tracingHandler());