UNPKG

@sentry/node

Version:

Sentry Node SDK using OpenTelemetry for performance instrumentation

162 lines (142 loc) 4.94 kB
import { ExpressInstrumentation } from '@opentelemetry/instrumentation-express'; import { defineIntegration, getIsolationScope, getDefaultIsolationScope, debug, spanToJSON, SEMANTIC_ATTRIBUTE_SENTRY_OP, httpRequestToRequestData, captureException } from '@sentry/core'; import { generateInstrumentOnce, addOriginToSpan, ensureIsWrapped } from '@sentry/node-core'; import { DEBUG_BUILD } from '../../debug-build.js'; const INTEGRATION_NAME = 'Express'; function requestHook(span) { addOriginToSpan(span, 'auto.http.otel.express'); const attributes = spanToJSON(span).data; // this is one of: middleware, request_handler, router const type = attributes['express.type']; if (type) { span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, `${type}.express`); } // Also update the name, we don't need to "middleware - " prefix const name = attributes['express.name']; if (typeof name === 'string') { span.updateName(name); } } function spanNameHook(info, defaultName) { if (getIsolationScope() === getDefaultIsolationScope()) { DEBUG_BUILD && debug.warn('Isolation scope is still default isolation scope - skipping setting transactionName'); return defaultName; } if (info.layerType === 'request_handler') { // type cast b/c Otel unfortunately types info.request as any :( const req = info.request ; const method = req.method ? req.method.toUpperCase() : 'GET'; getIsolationScope().setTransactionName(`${method} ${info.route}`); } return defaultName; } const instrumentExpress = generateInstrumentOnce( INTEGRATION_NAME, () => new ExpressInstrumentation({ requestHook: span => requestHook(span), spanNameHook: (info, defaultName) => spanNameHook(info, defaultName), }), ); const _expressIntegration = (() => { return { name: INTEGRATION_NAME, setupOnce() { instrumentExpress(); }, }; }) ; /** * Adds Sentry tracing instrumentation for [Express](https://expressjs.com/). * * If you also want to capture errors, you need to call `setupExpressErrorHandler(app)` after you set up your Express server. * * For more information, see the [express documentation](https://docs.sentry.io/platforms/javascript/guides/express/). * * @example * ```javascript * const Sentry = require('@sentry/node'); * * Sentry.init({ * integrations: [Sentry.expressIntegration()], * }) * ``` */ const expressIntegration = defineIntegration(_expressIntegration); /** * An Express-compatible error handler. */ function expressErrorHandler(options) { return function sentryErrorMiddleware( error, request, res, next, ) { const normalizedRequest = httpRequestToRequestData(request); // Ensure we use the express-enhanced request here, instead of the plain HTTP one // When an error happens, the `expressRequestHandler` middleware does not run, so we set it here too getIsolationScope().setSDKProcessingMetadata({ normalizedRequest }); const shouldHandleError = options?.shouldHandleError || defaultShouldHandleError; if (shouldHandleError(error)) { const eventId = captureException(error, { mechanism: { type: 'middleware', handled: false } }); (res ).sentry = eventId; } next(error); }; } function expressRequestHandler() { return function sentryRequestMiddleware( request, _res, next, ) { const normalizedRequest = httpRequestToRequestData(request); // Ensure we use the express-enhanced request here, instead of the plain HTTP one getIsolationScope().setSDKProcessingMetadata({ normalizedRequest }); next(); }; } /** * Add an Express error handler to capture errors to Sentry. * * The error handler must be before any other middleware and after all controllers. * * @param app The Express instances * @param options {ExpressHandlerOptions} Configuration options for the handler * * @example * ```javascript * const Sentry = require('@sentry/node'); * const express = require("express"); * * const app = express(); * * // Add your routes, etc. * * // Add this after all routes, * // but before any and other error-handling middlewares are defined * Sentry.setupExpressErrorHandler(app); * * app.listen(3000); * ``` */ function setupExpressErrorHandler( app, options, ) { app.use(expressRequestHandler()); app.use(expressErrorHandler(options)); ensureIsWrapped(app.use, 'express'); } function getStatusCodeFromResponse(error) { const statusCode = error.status || error.statusCode || error.status_code || error.output?.statusCode; return statusCode ? parseInt(statusCode , 10) : 500; } /** Returns true if response code is internal server error */ function defaultShouldHandleError(error) { const status = getStatusCodeFromResponse(error); return status >= 500; } export { expressErrorHandler, expressIntegration, instrumentExpress, setupExpressErrorHandler }; //# sourceMappingURL=express.js.map