import 'reflect-metadata';
import {accept, Filter} from '../decorators/filters';
import {Lookup} from '../decorators/lookup';
import {logger, MSG_FMT} from '../internal/log';
import {Providers} from '../providers';
import {joinFqn} from '../util';
import {Log} from './log';
import {LogLevel} from './LogLevel';
export type LogInvocationOptions = {
name?: string;
parent?: Log;
level: LogLevel;
includeResult?: boolean;
filter?: Filter;
}
/**
* Wrap and log a function invocation.
* ```typescript
* @LogInvocation() function f(a:string) {}
* f('A');
* // > f(A)
* ```
* @param level the level to log at
* @param includeResult if true will log the result of the function call as well.
* @param filter optional Filter to enable/disable logging.
* @constructor
*/
export function LogInvocation(options: LogInvocationOptions): MethodDecorator {
return function (target: any, propertyKey: string | symbol, descriptor: PropertyDescriptor): PropertyDescriptor {
const original = descriptor.value;
const fqn = options?.name || joinFqn(propertyKey, target.constructor.name);
const internalLog = logger('@LogInvocation');
internalLog.debug(MSG_FMT.annotatingMethod, target.constructor.name, propertyKey, 'LogInvocation', options);
const lookup = new Lookup<Log, Log>(() => Providers.provide(Log));
descriptor.value = function (...args: unknown[]): unknown {
let result;
Eif (accept(options)) {
const log = lookup.getInstance(this, fqn, () => {
const factory = lookup.getFactory(Log, fqn, options.parent);
return factory?.extend(fqn);
});
Iif (!log) {
internalLog.warn(
'Unable to decorate %s.%s with @LogInvocation! Logging has not been configured!',
target.constructor.name,
propertyKey,
);
}
const params = [...args].map((val: unknown) => JSON.stringify(val)).join(', ');
Eif (!options.includeResult) {
log?.write(
options.level,
'%s.%s(%s)',
target.constructor.name,
propertyKey,
params,
);
}
result = original.apply(this, args);
Iif (options.includeResult) {
log?.write(
options.level,
'%s.%s(%s) >> %o',
target.constructor.name,
propertyKey,
args,
result,
);
}
}
else {
result = original.apply(this, args);
}
return result;
};
return descriptor;
}
}
|