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
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118 | 1
1
1
1
1
1
1
1
3
3
3
3
3
6
3
8
8
6
6
6
6
5
1
2
2
2
2
2
3
2
3
3
3
3
3
3
3
| import {acceptFilter, Filter} from '../decorators';
import {Lookup} from '../decorators/lookup';
import {logger, MSG_FMT} from '../internal/log';
import {Providers} from '../providers';
import {joinFqn, Tags} from '../util';
export abstract class TimerWrapper<T> {
abstract wrap(func: (...args: unknown[]) => T): (...args: unknown[]) => T;
}
export abstract class TimerWrapperFactory {
abstract timer<T>(name: string, tags?: Tags): TimerWrapper<T>;
abstract asyncTimer<T>(name: string, tags?: Tags): TimerWrapper<Promise<T>>;
}
export type TimedOptions = {
name?: string;
timerWrapper?: TimerWrapperFactory;
tags?: Tags;
filter?: Filter;
}
type NotPromise<T> = Exclude<T, Promise<T>>;
export function Timed<T, P extends NotPromise<T>>(options: TimedOptions = {}): MethodDecorator {
return function timedMethod(target: Record<string, any>, prop: PropertyKey, desc: PropertyDescriptor): void {
const original = desc.value;
const fqn = options.name || joinFqn(prop, target.constructor.name);
const log = logger('@Timed');
log.debug(MSG_FMT.annotatingMethod, target.constructor.name, prop, 'Timed', options);
const lookup = new Lookup<TimerWrapperFactory, TimerWrapper<any>>(() => Providers.provide(TimerWrapperFactory));
desc.value = function timed(...args: unknown[]): T {
const apply = (): T => original.apply(this, ...args);
return acceptFilter({
filter: options,
yes: () => {
let _result: T;
const wrapper = lookup.getInstance(
this,
fqn,
() => lookup.getFactory(this, fqn, options.timerWrapper)?.asyncTimer(fqn, options.tags),
);
Eif (wrapper) {
_result = wrapper.wrap(apply)();
}
else {
_result = apply();
log.warn(
'Unable to decorate %s.%s with @TimedAsync() no TimerWrapperFactory provider to wrap call!',
target.constructor.name,
prop,
);
}
return _result;
}
,
no: apply,
});
};
}
}
export function TimedAsync<T, P extends Promise<T>>(options: TimedOptions = {}): MethodDecorator {
return function timedMethod(target: Record<string, any>, prop: PropertyKey, desc: PropertyDescriptor): void {
const original = desc.value;
const fqn = options.name || joinFqn(prop, target.constructor.name);
const log = logger('@TimedAsync');
log.debug(MSG_FMT.annotatingMethod, target.constructor.name, prop, 'Timed', options);
const lookup = new Lookup<TimerWrapperFactory, TimerWrapper<any>>(() => Providers.provide(TimerWrapperFactory));
desc.value = function timed(...args: unknown[]): Promise<T> {
const apply = (): Promise<T> => original.apply(this, ...args);
return acceptFilter({
filter: options,
yes: () => {
let _result: Promise<T>;
const wrapper = lookup.getInstance(
this,
fqn,
() => lookup.getFactory(this, fqn, options.timerWrapper)?.asyncTimer(fqn, options.tags),
);
Eif (wrapper) {
_result = wrapper.wrap(apply)();
}
else {
_result = apply();
log.warn(
'Unable to decorate %s.%s with @TimedAsync() no TimerWrapperFactory provider to wrap call!',
target.constructor.name,
prop,
);
}
return _result;
}
,
no: apply,
});
};
}
}
|