在装饰器中使用全局嵌套模块

flo*_*nkt 2 nestjs

我在 nest 中有一个全局记录器模块,它记录到云日志服务。我正在尝试创建一个添加日志记录功能的类方法装饰器。但是我正在努力如何在装饰器中注入全局嵌套模块的服务,因为我在文档中找到的所有依赖注入机制依赖都是基于类或类属性的注入。

export function logDecorator() {

  // I would like to inject a LoggerService that is a provider of a global logger module
  let logger = ???

  return (target: any, propertyKey: string, propertyDescriptor: PropertyDescriptor) => {
    //get original method
    const originalMethod = propertyDescriptor.value;

    //redefine descriptor value within own function block
    propertyDescriptor.value = function(...args: any[]) {
      logger.log(`${propertyKey} method called with args.`);
      
      //attach original method implementation
      const result = originalMethod.apply(this, args);

      //log result of method
      logger.log(`${propertyKey} method return value`);
    };
  };
}
Run Code Online (Sandbox Code Playgroud)

更新:每个请求一个简单的例子 基本的例子是使用我的自定义记录器(在我的例子中记录到云服务)记录对服务方法的调用:

class MyService {
    @logDecorator()
    someMethod(name: string) {
        // calls to this method as well as method return values would be logged to CloudWatch
        return `Hello ${name}`
    }
}
Run Code Online (Sandbox Code Playgroud)

另一个扩展用例是捕获一些错误,然后记录它们。我有很多这种逻辑可以在我的所有服务中重用。

flo*_*nkt 14

好的,找到了解决办法。以防其他人偶然发现这一点。首先请记住装饰器是如何工作的——它们是基于类构造函数的,而不是基于实例的。

就我而言,我希望将我的记录器服务注入到类实例中。所以解决办法就是告诉装饰器中的Nest,将LoggerService注入到包含被装饰方法的类的实例中。

import { Inject } from '@nestjs/common';
import { LoggerService } from '../../logger/logger.service';

export function logErrorDecorator(bubble = true) {
  const injectLogger = Inject(LoggerService);

  return (target: any, propertyKey: string, propertyDescriptor: PropertyDescriptor) => {
    injectLogger(target, 'logger'); // this is the same as using constructor(private readonly logger: LoggerService) in a class

    //get original method
    const originalMethod = propertyDescriptor.value;

    //redefine descriptor value within own function block
    propertyDescriptor.value = async function(...args: any[]) {
      try {
        return await originalMethod.apply(this, args);
      } catch (error) {
        const logger: LoggerService = this.logger;

        logger.setContext(target.constructor.name);
        logger.error(error.message, error.stack);

        // rethrow error, so it can bubble up
        if (bubble) {
          throw error;
        }
      }
    };
  };
}
Run Code Online (Sandbox Code Playgroud)

这提供了在方法中捕获错误、将它们记录在服务上下文中并重新抛出它们(以便您的控制器可以处理用户响应)或不重新抛出的可能性。就我而言,我还必须在这里实现一些与事务相关的逻辑。

export class FoobarService implements OnModuleInit {
  onModuleInit() {
    this.test();
  }

  @logErrorDecorator()
  test() {
    throw new Error('Oh my');
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 当我使用这种方法时,服务最终在描述值函数中未定义。就我而言,我使用的服务还依赖于其他两个服务,因此这可能会产生影响。这个解决方案可以用于注入其他服务的服务吗? (4认同)
  • 嘿@Albert,您是否发现导致您的服务未定义的原因? (2认同)