@sentry/node
Version:
Sentry Node SDK using OpenTelemetry for performance instrumentation
134 lines (117 loc) • 4.86 kB
JavaScript
import { _optionalChain } from '@sentry/utils';
import { ExpressInstrumentation } from '@opentelemetry/instrumentation-express';
import { defineIntegration, spanToJSON, SEMANTIC_ATTRIBUTE_SENTRY_OP, getIsolationScope, getDefaultIsolationScope, getClient, captureException } from '@sentry/core';
import { logger } from '@sentry/utils';
import { DEBUG_BUILD } from '../../debug-build.js';
import { generateInstrumentOnce } from '../../otel/instrument.js';
import { addOriginToSpan } from '../../utils/addOriginToSpan.js';
import { ensureIsWrapped } from '../../utils/ensureIsWrapped.js';
const INTEGRATION_NAME = 'Express';
const instrumentExpress = generateInstrumentOnce(
INTEGRATION_NAME,
() =>
new ExpressInstrumentation({
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);
}
},
spanNameHook(info, defaultName) {
if (getIsolationScope() === getDefaultIsolationScope()) {
DEBUG_BUILD &&
logger.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 _expressIntegration = (() => {
return {
name: INTEGRATION_NAME,
setupOnce() {
instrumentExpress();
},
};
}) ;
/**
* Express integration
*
* Capture tracing data for express.
* In order to capture exceptions, you have to call `setupExpressErrorHandler(app)` before any other middleware and after all controllers.
*/
const expressIntegration = defineIntegration(_expressIntegration);
/**
* An Express-compatible error handler.
*/
function expressErrorHandler(options) {
return function sentryErrorMiddleware(
error,
_req,
res,
next,
) {
const shouldHandleError = _optionalChain([options, 'optionalAccess', _ => _.shouldHandleError]) || defaultShouldHandleError;
if (shouldHandleError(error)) {
const client = getClient();
if (client && client.getOptions().autoSessionTracking) {
// Check if the `SessionFlusher` is instantiated on the client to go into this branch that marks the
// `requestSession.status` as `Crashed`, and this check is necessary because the `SessionFlusher` is only
// instantiated when the the`requestHandler` middleware is initialised, which indicates that we should be
// running in SessionAggregates mode
const isSessionAggregatesMode = client['_sessionFlusher'] !== undefined;
if (isSessionAggregatesMode) {
const requestSession = getIsolationScope().getRequestSession();
// If an error bubbles to the `errorHandler`, then this is an unhandled error, and should be reported as a
// Crashed session. The `_requestSession.status` is checked to ensure that this error is happening within
// the bounds of a request, and if so the status is updated
if (requestSession && requestSession.status !== undefined) {
requestSession.status = 'crashed';
}
}
}
const eventId = captureException(error, { mechanism: { type: 'middleware', handled: false } });
(res ).sentry = eventId;
next(error);
return;
}
next(error);
};
}
/**
* Setup an error handler for Express.
* The error handler must be before any other middleware and after all controllers.
*/
function setupExpressErrorHandler(
app,
options,
) {
app.use(expressErrorHandler(options));
ensureIsWrapped(app.use, 'express');
}
function getStatusCodeFromResponse(error) {
const statusCode = error.status || error.statusCode || error.status_code || (error.output && 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