我在嵌套中有一个全局记录器模块,该模块记录到云记录服务。我试图创建一个添加日志记录功能的类方法装饰器。但是我正在努力如何在装饰器中注入全局嵌套模块的服务,因为我在文档中发现的所有依赖项注入机制都依赖于基于类或类属性的注入。
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`);
};
};
}
更新:按要求提供一个简单示例 基本示例是使用我的自定义记录器(对我而言,它记录到云服务)记录对服务方法的调用:
class MyService {
@logDecorator()
someMethod(name: string) {
// calls to this method as well as method return values would be logged to CloudWatch
return `Hello ${name}`
}
}
另一个扩展用例是捕获一些错误,然后将其记录下来。我有很多这种逻辑可以在我所有的服务中重复使用。
答案 0 :(得分:6)
好的,找到了解决方案。万一其他人偶然发现了这个。首先,请记住装饰器的工作原理-它们是基于类构造函数的,而不是基于实例的。
就我而言,我想将记录器服务注入类实例中。因此,解决方案是告诉装饰器中的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;
}
}
};
};
}
这样可以捕获方法中的错误,在服务上下文中记录错误,然后重新抛出错误(以便您的控制器可以处理用户响应)。就我而言,我还必须在这里实现一些与事务相关的逻辑。
export class FoobarService implements OnModuleInit {
onModuleInit() {
this.test();
}
@logErrorDecorator()
test() {
throw new Error('Oh my');
}
}