diff options
Diffstat (limited to 'u/server/metrics.ts')
-rw-r--r-- | u/server/metrics.ts | 112 |
1 files changed, 112 insertions, 0 deletions
diff --git a/u/server/metrics.ts b/u/server/metrics.ts new file mode 100644 index 0000000..05df967 --- /dev/null +++ b/u/server/metrics.ts @@ -0,0 +1,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"), + ); + } +} |