-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
/
Copy pathnest.ts
129 lines (110 loc) · 4 KB
/
nest.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
import { NestInstrumentation } from '@opentelemetry/instrumentation-nestjs-core';
import {
SEMANTIC_ATTRIBUTE_SENTRY_OP,
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
captureException,
defineIntegration,
getClient,
getDefaultIsolationScope,
getIsolationScope,
spanToJSON,
} from '@sentry/core';
import type { IntegrationFn, Span } from '@sentry/types';
import { logger } from '@sentry/utils';
import { generateInstrumentOnce } from '../../../otel/instrument';
import { SentryNestInstrumentation } from './sentry-nest-instrumentation';
import type { MinimalNestJsApp, NestJsErrorFilter } from './types';
const INTEGRATION_NAME = 'Nest';
const instrumentNestCore = generateInstrumentOnce('Nest-Core', () => {
return new NestInstrumentation();
});
const instrumentNestCommon = generateInstrumentOnce('Nest-Common', () => {
return new SentryNestInstrumentation();
});
export const instrumentNest = Object.assign(
(): void => {
instrumentNestCore();
instrumentNestCommon();
},
{ id: INTEGRATION_NAME },
);
const _nestIntegration = (() => {
return {
name: INTEGRATION_NAME,
setupOnce() {
instrumentNest();
},
};
}) satisfies IntegrationFn;
/**
* Nest framework integration
*
* Capture tracing data for nest.
*/
export const nestIntegration = defineIntegration(_nestIntegration);
/**
* Setup an error handler for Nest.
*/
export function setupNestErrorHandler(app: MinimalNestJsApp, baseFilter: NestJsErrorFilter): void {
// 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(`${req.method?.toUpperCase() || '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: unknown, host: unknown) => {
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: Span): void {
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`,
});
}