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

Posted

技术标签:

【中文标题】如何在nestjs拦截器中检测控制器?【英文标题】:How to instrument a controller in nestjs interceptor? 【发布时间】:2021-02-11 05:02:41 【问题描述】:

我想为 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;
    

【问题讨论】:

您能否再解释一下您所说的仪器是什么意思? 当然,我的意思是:跟踪函数执行以确定执行时间(由例如en.wikipedia.org/wiki/Instrumentation_(computer_programming) 定义)并由 APM 解决方案提供,例如docs.datadoghq.com/tracing/guide/instrument_custom_method/… 啊,我明白了。好吧,从来没有使用过dd-trace,但我确实有a custom logger 有一个日志拦截器,它可以以DataDog 友好的格式输出数据。同时我会调查dd-trace 这里远射,但我认为你想要而不是observable.subscribe() 是做observable.pipe(tap(resolve)),其中tap 是从rxjs/operators 导入的。如果可行,我会为答案写一个详细的解释 我不会说这是 Nest 的问题,更像是 RxJS 的问题,因为 它是 的设计方式。不过,Nest 使用它的事实确实使它在这种情况下很不幸。我认为在这种情况下你必须做的是从d-trace 创建一个自定义的Span 对象并按照你的期望设置值。我在前面的评论中提到的自定义记录器有点这样做(我不知道我这样做了),所以你可以从中得到一些想法。如果您想进一步讨论这个问题,请随时reach out on Discord。用户名:PerfectOrphan31#6003 【参考方案1】:

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

【讨论】:

以上是关于如何在nestjs拦截器中检测控制器?的主要内容,如果未能解决你的问题,请参考以下文章

NestJs - 如何在拦截器上获取请求正文

如何测试 Nestjs 拦截器?

如何在 neo4j-graphql-js 端点中使用带有 nestjs 框架的拦截器?

如何在发送 Nestjs 之前格式化响应?

如何在 NestJS 中记录所有 Axios 外部 http 请求

如何从控制器在nestjs应用程序中发送错误代码?