|
import { |
|
CallHandler, |
|
ExecutionContext, |
|
Inject, |
|
Injectable, |
|
NestInterceptor, |
|
} from "@nestjs/common"; |
|
import { GqlContextType, GqlExecutionContext } from "@nestjs/graphql"; |
|
import { InjectSentry, SentryService } from "nestjs-sentry"; |
|
import * as Sentry from "@sentry/node"; |
|
import { SpanStatus } from "@sentry/tracing"; |
|
import { Observable } from "rxjs"; |
|
import { tap } from "rxjs/operators"; |
|
|
|
import { runWithSentry } from "./hub"; |
|
import { |
|
SentryUserProvider, |
|
SENTRY_USER_PROVIDER, |
|
} from "./sentry-user.provider"; |
|
|
|
@Injectable() |
|
export class TracingInterceptor implements NestInterceptor { |
|
constructor( |
|
@InjectSentry() private sentry: SentryService, |
|
@Inject(SENTRY_USER_PROVIDER) |
|
private sentryUserProvider: SentryUserProvider |
|
) {} |
|
|
|
async intercept( |
|
context: ExecutionContext, |
|
next: CallHandler |
|
): Promise<Observable<unknown>> { |
|
if (context.getType<GqlContextType>() !== "graphql") { |
|
// only handle graphql requests |
|
return next.handle(); |
|
} |
|
|
|
const client = this.sentry.instance().getCurrentHub().getClient(); |
|
const hub = new Sentry.Hub(client); |
|
|
|
const gqlContext = GqlExecutionContext.create(context).getContext(); |
|
|
|
const { user, tags } = await this.sentryUserProvider.getSentryUser( |
|
gqlContext |
|
); |
|
const req = gqlContext.req; |
|
|
|
const transaction = hub.startTransaction({ |
|
op: "resolver", |
|
name: [context.getClass().name, context.getHandler().name].join("."), |
|
tags: { |
|
class: context.getClass().name, |
|
handler: context.getHandler().name, |
|
...tags, |
|
}, |
|
}); |
|
|
|
hub.configureScope((scope) => { |
|
scope.setSpan(transaction); |
|
|
|
scope.setUser(user); |
|
}); |
|
|
|
return new Observable((observer) => { |
|
runWithSentry(transaction, () => { |
|
next |
|
.handle() |
|
.pipe( |
|
tap({ |
|
complete: () => { |
|
transaction.finish(); |
|
}, |
|
error: (exception) => { |
|
transaction.setStatus(SpanStatus.InternalError); |
|
transaction.finish(); |
|
hub.withScope((scope) => { |
|
if (req) { |
|
const data = Sentry.Handlers.parseRequest({}, req); |
|
scope.setExtra("req", data.request); |
|
if (data.extra) scope.setExtras(data.extra); |
|
} |
|
scope.setUser(user); |
|
|
|
hub.captureException(exception); |
|
}); |
|
}, |
|
}) |
|
) |
|
.subscribe({ |
|
next: (res) => observer.next(res), |
|
error: (error) => observer.error(error), |
|
complete: () => observer.complete(), |
|
}); |
|
}); |
|
}); |
|
} |
|
} |