feat(core-shared): IMetrics interface + NoopMetrics impl
This commit is contained in:
@@ -0,0 +1,36 @@
|
|||||||
|
export type MetricAttributeValue = string | number | boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Vendor-neutral metrics signal interface. Mirrors the pattern of ITracer / ILogger.
|
||||||
|
* Three impls: NoopMetrics (noop), OtelMetrics (OTel API), RecordingMetrics (core-testing).
|
||||||
|
*
|
||||||
|
* gauge() limitation: uses UpDownCounter under the hood, which accumulates deltas.
|
||||||
|
* True "set to absolute value" semantics require ObservableGauge with a callback —
|
||||||
|
* deferred to a v2 interface when the first true-gauge use case lands.
|
||||||
|
*/
|
||||||
|
export interface IMetrics {
|
||||||
|
/** Monotonic counter. Use for event counts (signups, errors, requests). */
|
||||||
|
counter(
|
||||||
|
name: string,
|
||||||
|
value?: number,
|
||||||
|
attributes?: Record<string, MetricAttributeValue>,
|
||||||
|
): void;
|
||||||
|
|
||||||
|
/** Distribution. Use for measured quantities (latency, payload size). */
|
||||||
|
histogram(
|
||||||
|
name: string,
|
||||||
|
value: number,
|
||||||
|
attributes?: Record<string, MetricAttributeValue>,
|
||||||
|
): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Point-in-time value. UpDownCounter under the hood — true gauge semantics
|
||||||
|
* (set to absolute value) require ObservableGauge with an async callback;
|
||||||
|
* that is deferred to a future v2 spec when the first true-gauge use case arrives.
|
||||||
|
*/
|
||||||
|
gauge(
|
||||||
|
name: string,
|
||||||
|
value: number,
|
||||||
|
attributes?: Record<string, MetricAttributeValue>,
|
||||||
|
): void;
|
||||||
|
}
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
import { NoopMetrics } from "./noop-metrics";
|
||||||
|
|
||||||
|
describe("NoopMetrics", () => {
|
||||||
|
it("counter() returns undefined without throwing", () => {
|
||||||
|
const metrics = new NoopMetrics();
|
||||||
|
expect(() => metrics.counter("my.counter")).not.toThrow();
|
||||||
|
expect(() => metrics.counter("my.counter", 5)).not.toThrow();
|
||||||
|
expect(() =>
|
||||||
|
metrics.counter("my.counter", 1, { feature: "auth", success: true }),
|
||||||
|
).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("histogram() returns undefined without throwing", () => {
|
||||||
|
const metrics = new NoopMetrics();
|
||||||
|
expect(() => metrics.histogram("my.latency", 42)).not.toThrow();
|
||||||
|
expect(() =>
|
||||||
|
metrics.histogram("my.latency", 100, { route: "/api/me" }),
|
||||||
|
).not.toThrow();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("gauge() returns undefined without throwing", () => {
|
||||||
|
const metrics = new NoopMetrics();
|
||||||
|
expect(() => metrics.gauge("queue.depth", 7)).not.toThrow();
|
||||||
|
expect(() =>
|
||||||
|
metrics.gauge("queue.depth", 3, { queue: "emails" }),
|
||||||
|
).not.toThrow();
|
||||||
|
});
|
||||||
|
});
|
||||||
21
packages/core-shared/src/instrumentation/noop-metrics.ts
Normal file
21
packages/core-shared/src/instrumentation/noop-metrics.ts
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
import type { IMetrics, MetricAttributeValue } from "./metrics.interface";
|
||||||
|
|
||||||
|
export class NoopMetrics implements IMetrics {
|
||||||
|
counter(
|
||||||
|
_name: string,
|
||||||
|
_value?: number,
|
||||||
|
_attributes?: Record<string, MetricAttributeValue>,
|
||||||
|
): void {}
|
||||||
|
|
||||||
|
histogram(
|
||||||
|
_name: string,
|
||||||
|
_value: number,
|
||||||
|
_attributes?: Record<string, MetricAttributeValue>,
|
||||||
|
): void {}
|
||||||
|
|
||||||
|
gauge(
|
||||||
|
_name: string,
|
||||||
|
_value: number,
|
||||||
|
_attributes?: Record<string, MetricAttributeValue>,
|
||||||
|
): void {}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user