如何在NestJS拦截器中检测控制器?

时间:2020-10-28 18:50:41

标签: node.js typescript rxjs nestjs datadog

我想检测用于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;
    }
}

1 个答案:

答案 0 :(得分:0)

使用 OpenTelemetry-js 时遇到了类似的问题,为了设置正确的范围,我必须将 handle() Observable 包装到 Async 承诺中以设置上下文,然后再次将承诺包装为 Observablerxjs 管道 (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;
    }))