我想检测用于APM的nestjs控制器的每种方法。 我编写了以下拦截器,以检测控制器的调用。
但是,我不知道如何正确包装对next.handle()
的调用。
我没有使用RxJS Observables的经验。
问题:是否可以正确包装调用?如果可以,如何包装?
当前的方法似乎可以衡量控制器的执行时间,但不能为控制器的方法设置正确的跟踪范围。我想问题是next.handle()
也必须包装。
import { CallHandler, ExecutionContext, Injectable, NestInterceptor } from "@nestjs/common";
import { Reflector } from "@nestjs/core";
import { Observable } from "rxjs";
import { PATH_METADATA } from '@nestjs/common/constants';
import tracer from "dd-trace";
@Injectable()
export class ApmInterceptor implements NestInterceptor {
constructor(private readonly reflector: Reflector) {}
public intercept(context: ExecutionContext, next: CallHandler): Observable<unknown> {
const request: Request = context.switchToHttp().getRequest();
const path = this.reflector.get<string[]>(PATH_METADATA, context.getHandler());
const method = request.method;
const observable = next.handle();
tracer.trace(`[${method}] ${path}`, () => new Promise((resolve, reject) => {
observable.subscribe({
complete: resolve,
});
}));
return observable;
}
}
答案 0 :(得分:0)
使用 OpenTelemetry-js 时遇到了类似的问题,为了设置正确的范围,我必须将 handle()
Observable
包装到 Async
承诺中以设置上下文,然后再次将承诺包装为 Observable
的 rxjs
管道 (Observable
-> Promise
-> Observable
)
import {from, Observable} from 'rxjs';
...
async intercept(executionContext: ExecutionContext, next: CallHandler): Promise<Observable<any>> {
const request: Request = context.switchToHttp().getRequest();
const path = this.reflector.get<string[]>(PATH_METADATA, context.getHandler());
const method = request.method;
const observable = tracer.trace(`[${method}] ${path}`, () => new Promise((resolve, reject) => {
return next.handle().toPromise();
}));
return observable.pipe(
map(value => {
// Here you can stop your trace manually
return value;
}),
catchError(error => {
// Here you can stop your trace manually
throw error;
}))
}
对于 OpenTelemetry,您必须创建/停止跨度并设置正确的上下文:
const span = trace.getTracer('default').startSpan(spanName);
const observable = from(context.with(trace.setSpan(context.active(), span), async () => {
return next.handle().toPromise();
}));
return observable.pipe(
map(value => {
span.stop();
return value;
}),
catchError(error => {
span.addEvent('error', {error: error});
span.stop();
throw error;
}))