Use global nest module in decorator

て烟熏妆下的殇ゞ 提交于 2020-05-09 11:02:51

问题


I have a global logger module in nest, that logs to a cloud logging service. I am trying to create a class method decorator that adds logging functionality. But I am struggling how to inject the service of a global nest module inside a decorator, since all dependency injection mechanisms I found in the docs depend are class or class property based injection.

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`);
    };
  };
}

UPDATE: Per reqest a simple example Basic example would be to log calls to a service method using my custom logger (which in my case logs to a cloud service):

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

Another extended use case would be to catch some errors, then log them. I have a lot of this kind of logic that get reused across all my services.


回答1:


Okay, found a solution. In case anyone else stumbles upon this. First please keep in mind how decorators work – they are class constructor based, not instance based.

In my case I wanted to have my logger service injected in the class instance. So the solution is to tell Nest in the decorator to inject the LoggerService into the instance of the class that contains the decorated method.

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;
        }
      }
    };
  };
}

This gives the possibility to catch errors in a method, log them within the service context, and either re-throw them (so your controllers can handle user resp) or not. In my case I also had to implement some transaction-related logic here.

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

  @logErrorDecorator()
  test() {
    throw new Error('Oh my');
  }
}


来源:https://stackoverflow.com/questions/60578332/use-global-nest-module-in-decorator

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!