-
-
Notifications
You must be signed in to change notification settings - Fork 1.7k
/
Copy pathsetup.ts
248 lines (223 loc) · 8.03 KB
/
setup.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
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
import type {
ArgumentsHost,
CallHandler,
DynamicModule,
ExecutionContext,
NestInterceptor,
OnModuleInit,
} from '@nestjs/common';
import { Catch, Global, HttpException, Injectable, Logger, Module } from '@nestjs/common';
import type { HttpServer } from '@nestjs/common';
import { APP_INTERCEPTOR, BaseExceptionFilter } from '@nestjs/core';
import {
SEMANTIC_ATTRIBUTE_SENTRY_OP,
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
captureException,
getClient,
getDefaultIsolationScope,
getIsolationScope,
spanToJSON,
} from '@sentry/core';
import type { Span } from '@sentry/types';
import { logger } from '@sentry/utils';
import type { Observable } from 'rxjs';
import { isExpectedError } from './helpers';
/**
* Note: We cannot use @ syntax to add the decorators, so we add them directly below the classes as function wrappers.
*/
/**
* Interceptor to add Sentry tracing capabilities to Nest.js applications.
*
* @deprecated `SentryTracingInterceptor` is deprecated.
* If you are using `@sentry/nestjs` you can safely remove any references to the `SentryTracingInterceptor`.
* If you are using another package migrate to `@sentry/nestjs` and remove the `SentryTracingInterceptor` afterwards.
*/
class SentryTracingInterceptor implements NestInterceptor {
// used to exclude this class from being auto-instrumented
public readonly __SENTRY_INTERNAL__: boolean;
public constructor() {
this.__SENTRY_INTERNAL__ = true;
}
/**
* Intercepts HTTP requests to set the transaction name for Sentry tracing.
*/
public intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
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();
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
if (req.route) {
// eslint-disable-next-line @sentry-internal/sdk/no-optional-chaining,@typescript-eslint/no-unsafe-member-access
getIsolationScope().setTransactionName(`${req.method?.toUpperCase() || 'GET'} ${req.route.path}`);
}
}
return next.handle();
}
}
// eslint-disable-next-line deprecation/deprecation
Injectable()(SentryTracingInterceptor);
// eslint-disable-next-line deprecation/deprecation
export { SentryTracingInterceptor };
/**
* Global filter to handle exceptions and report them to Sentry.
*/
class SentryGlobalFilter extends BaseExceptionFilter {
public readonly __SENTRY_INTERNAL__: boolean;
private readonly _logger: Logger;
public constructor(applicationRef?: HttpServer) {
super(applicationRef);
this.__SENTRY_INTERNAL__ = true;
this._logger = new Logger('ExceptionsHandler');
}
/**
* Catches exceptions and reports them to Sentry unless they are expected errors.
*/
public catch(exception: unknown, host: ArgumentsHost): void {
// The BaseExceptionFilter does not work well in GraphQL applications.
// By default, Nest GraphQL applications use the ExternalExceptionFilter, which just rethrows the error:
// https://github.com/nestjs/nest/blob/master/packages/core/exceptions/external-exception-filter.ts
if (host.getType<'graphql'>() === 'graphql') {
// neither report nor log HttpExceptions
if (exception instanceof HttpException) {
throw exception;
}
if (exception instanceof Error) {
this._logger.error(exception.message, exception.stack);
}
captureException(exception);
throw exception;
}
if (!isExpectedError(exception)) {
captureException(exception);
}
return super.catch(exception, host);
}
}
Catch()(SentryGlobalFilter);
export { SentryGlobalFilter };
/**
* Global filter to handle exceptions in NestJS + GraphQL applications and report them to Sentry.
*
* @deprecated `SentryGlobalGraphQLFilter` is deprecated. Use the `SentryGlobalFilter` instead. The `SentryGlobalFilter` is a drop-in replacement.
*/
class SentryGlobalGraphQLFilter {
private static readonly _logger = new Logger('ExceptionsHandler');
public readonly __SENTRY_INTERNAL__: boolean;
public constructor() {
this.__SENTRY_INTERNAL__ = true;
}
/**
* Catches exceptions and reports them to Sentry unless they are HttpExceptions.
*/
// eslint-disable-next-line @typescript-eslint/no-unused-vars
public catch(exception: unknown, host: ArgumentsHost): void {
// neither report nor log HttpExceptions
if (exception instanceof HttpException) {
throw exception;
}
if (exception instanceof Error) {
// eslint-disable-next-line deprecation/deprecation
SentryGlobalGraphQLFilter._logger.error(exception.message, exception.stack);
}
captureException(exception);
throw exception;
}
}
// eslint-disable-next-line deprecation/deprecation
Catch()(SentryGlobalGraphQLFilter);
// eslint-disable-next-line deprecation/deprecation
export { SentryGlobalGraphQLFilter };
/**
* Global filter to handle exceptions and report them to Sentry.
*
* This filter is a generic filter that can handle both HTTP and GraphQL exceptions.
*
* @deprecated `SentryGlobalGenericFilter` is deprecated. Use the `SentryGlobalFilter` instead. The `SentryGlobalFilter` is a drop-in replacement.
*/
export const SentryGlobalGenericFilter = SentryGlobalFilter;
/**
* Service to set up Sentry performance tracing for Nest.js applications.
*
* @deprecated `SentryService` is deprecated.
* If you are using `@sentry/nestjs` you can safely remove any references to the `SentryService`.
* If you are using another package migrate to `@sentry/nestjs` and remove the `SentryService` afterwards.
*/
class SentryService implements OnModuleInit {
public readonly __SENTRY_INTERNAL__: boolean;
public constructor() {
this.__SENTRY_INTERNAL__ = true;
}
/**
* Initializes the Sentry service and registers span attributes.
*/
public onModuleInit(): 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);
});
}
}
}
// eslint-disable-next-line deprecation/deprecation
Injectable()(SentryService);
// eslint-disable-next-line deprecation/deprecation
export { SentryService };
/**
* Set up a root module that can be injected in nest applications.
*/
class SentryModule {
/**
* Configures the module as the root module in a Nest.js application.
*/
public static forRoot(): DynamicModule {
return {
module: SentryModule,
providers: [
// eslint-disable-next-line deprecation/deprecation
SentryService,
{
provide: APP_INTERCEPTOR,
// eslint-disable-next-line deprecation/deprecation
useClass: SentryTracingInterceptor,
},
],
// eslint-disable-next-line deprecation/deprecation
exports: [SentryService],
};
}
}
Global()(SentryModule);
Module({
providers: [
// eslint-disable-next-line deprecation/deprecation
SentryService,
{
provide: APP_INTERCEPTOR,
// eslint-disable-next-line deprecation/deprecation
useClass: SentryTracingInterceptor,
},
],
// eslint-disable-next-line deprecation/deprecation
exports: [SentryService],
})(SentryModule);
export { SentryModule };
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`,
});
}