import { _optionalChain } from '@sentry/utils'; import { NestInstrumentation } from '@opentelemetry/instrumentation-nestjs-core'; import { defineIntegration, getClient, getIsolationScope, getDefaultIsolationScope, captureException, spanToJSON, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core'; import { logger } from '@sentry/utils'; import { generateInstrumentOnce } from '../../../otel/instrument.js'; import { SentryNestInstrumentation } from './sentry-nest-instrumentation.js'; const INTEGRATION_NAME = 'Nest'; const instrumentNestCore = generateInstrumentOnce('Nest-Core', () => { return new NestInstrumentation(); }); const instrumentNestCommon = generateInstrumentOnce('Nest-Common', () => { return new SentryNestInstrumentation(); }); const instrumentNest = Object.assign( () => { instrumentNestCore(); instrumentNestCommon(); }, { id: INTEGRATION_NAME }, ); const _nestIntegration = (() => { return { name: INTEGRATION_NAME, setupOnce() { instrumentNest(); }, }; }) ; /** * Nest framework integration * * Capture tracing data for nest. */ const nestIntegration = defineIntegration(_nestIntegration); /** * Setup an error handler for Nest. */ function setupNestErrorHandler(app, baseFilter) { // Sadly, NestInstrumentation has no requestHook, so we need to add the attributes here // We register this hook in this method, because if we register it in the integration `setup`, // it would always run even for users that are not even using Nest.js const client = getClient(); if (client) { client.on('spanStart', span => { addNestSpanAttributes(span); }); } app.useGlobalInterceptors({ intercept(context, next) { if (getIsolationScope() === getDefaultIsolationScope()) { logger.warn('Isolation scope is still the default isolation scope, skipping setting transactionName.'); return next.handle(); } if (context.getType() === 'http') { const req = context.switchToHttp().getRequest(); if (req.route) { getIsolationScope().setTransactionName(`${_optionalChain([req, 'access', _ => _.method, 'optionalAccess', _2 => _2.toUpperCase, 'call', _3 => _3()]) || 'GET'} ${req.route.path}`); } } return next.handle(); }, }); const wrappedFilter = new Proxy(baseFilter, { get(target, prop, receiver) { if (prop === 'catch') { const originalCatch = Reflect.get(target, prop, receiver); return (exception, host) => { const exceptionIsObject = typeof exception === 'object' && exception !== null; const exceptionStatusCode = exceptionIsObject && 'status' in exception ? exception.status : null; const exceptionErrorProperty = exceptionIsObject && 'error' in exception ? exception.error : null; /* Don't report expected NestJS control flow errors - `HttpException` errors will have a `status` property - `RpcException` errors will have an `error` property */ if (exceptionStatusCode !== null || exceptionErrorProperty !== null) { return originalCatch.apply(target, [exception, host]); } captureException(exception); return originalCatch.apply(target, [exception, host]); }; } return Reflect.get(target, prop, receiver); }, }); app.useGlobalFilters(wrappedFilter); } function addNestSpanAttributes(span) { const attributes = spanToJSON(span).data || {}; // this is one of: app_creation, request_context, handler const type = attributes['nestjs.type']; // If this is already set, or we have no nest.js span, no need to process again... if (attributes[SEMANTIC_ATTRIBUTE_SENTRY_OP] || !type) { return; } span.setAttributes({ [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.otel.nestjs', [SEMANTIC_ATTRIBUTE_SENTRY_OP]: `${type}.nestjs`, }); } export { instrumentNest, nestIntegration, setupNestErrorHandler }; //# sourceMappingURL=nest.js.map