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
|
import {
type BiMapper,
Either,
type IEither,
type ITraceable,
type Mapper,
type Supplier,
} from "@emprespresso/pengueno";
export enum Unit {
COUNT,
MILLISECONDS,
}
export interface IMetric<MetricT extends string, TUnit extends Unit> {
readonly metric: MetricT;
readonly unit: TUnit;
readonly value: number;
readonly emissionTimestamp: Date;
}
export type BaseMetricT = string;
export interface CountMetric<MetricT extends BaseMetricT>
extends IMetric<MetricT, Unit.COUNT> {
readonly unit: Unit.COUNT;
}
export interface TimeMetric<MetricT extends BaseMetricT>
extends IMetric<MetricT, Unit.MILLISECONDS> {
readonly unit: Unit.MILLISECONDS;
}
export interface IMetricsData<
MetricT extends BaseMetricT,
Tracing,
TraceW,
> {
addCount: BiMapper<MetricT, number, CountMetric<MetricT>>;
stopwatch: BiMapper<
MetricT,
ITraceable<Tracing, TraceW>,
ITraceable<MetricT, TraceW>
>;
endStopwatch: Mapper<
ITraceable<MetricT, TraceW>,
IEither<Error, TimeMetric<MetricT>>
>;
flush: Supplier<Array<IMetric<MetricT, Unit>>>;
}
export class TraceableMetricsData<MetricT extends BaseMetricT, Tracing, Trace>
implements IMetricsData<MetricT, Tracing, Trace> {
private readonly timers: Map<ITraceable<MetricT, Trace>, Date> = new Map();
private metricBuffer: Array<IMetric<MetricT, Unit>> = [];
private constructor() {}
private addMetric<TUnit extends Unit>(
metric: MetricT,
unit: TUnit,
value: number,
): IMetric<MetricT, TUnit> {
const _metric = {
metric,
unit,
value,
emissionTimestamp: new Date(),
};
this.metricBuffer.push(_metric);
return _metric;
}
public flush() {
const metrics = [...this.metricBuffer];
this.metricBuffer = [];
return metrics;
}
public addCount(
metric: MetricT,
count: number,
): CountMetric<MetricT> {
return this.addMetric(metric, Unit.COUNT, count);
}
public stopwatch(metric: MetricT, traceable: ITraceable<Tracing, Trace>) {
const timer = traceable.move(metric);
this.timers.set(timer, new Date());
return timer;
}
public endStopwatch(
stopwatch: ITraceable<MetricT, Trace>,
): IEither<Error, TimeMetric<MetricT>> {
const now = new Date();
if (this.timers.has(stopwatch)) {
const timer = this.timers.get(stopwatch)!;
const diff = now.getTime() - timer.getTime();
this.timers.delete(stopwatch);
return Either.right<Error, TimeMetric<MetricT>>(
this.addMetric(stopwatch.item, Unit.MILLISECONDS, diff) as TimeMetric<
MetricT
>,
);
}
return Either.left<Error, TimeMetric<MetricT>>(
new Error("cannot stop stopwatch before starting it"),
);
}
}
|