summaryrefslogtreecommitdiff
path: root/u/server/metrics.ts
diff options
context:
space:
mode:
authorElizabeth Hunt <lizhunt@amazon.com>2025-05-16 16:17:13 -0700
committerElizabeth Hunt <lizhunt@amazon.com>2025-05-16 16:17:13 -0700
commitef51b25e4388cbdf3a27e23d9f1fa381ae20a5ad (patch)
treed54be88fa30fd2da97a97fc7006d9ff9d94ed16a /u/server/metrics.ts
parent1ab20482ab37d7962c8e69701163270e687df3ca (diff)
downloadci-ef51b25e4388cbdf3a27e23d9f1fa381ae20a5ad.tar.gz
ci-ef51b25e4388cbdf3a27e23d9f1fa381ae20a5ad.zip
snapshot
Diffstat (limited to 'u/server/metrics.ts')
-rw-r--r--u/server/metrics.ts112
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"),
+ );
+ }
+}