Code coverage report for core/src/log/decorators.ts

Statements: 90.63% (29 / 32)      Branches: 57.69% (15 / 26)      Functions: 100% (6 / 6)      Lines: 90% (27 / 30)      Ignored: none     

All files » core/src/log/ » decorators.ts
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 891 1 1 1 1 1 1                                                 1 1 1 1   1 1   1   1   1 1 1 1     1               2 1 1               1 1                           1     1      
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;
  }
}