01 · Problem
What is it for?
Recreating a named metric on every call causes registry conflicts and loses a stable measurement contract.
02 · Mental model
How should you think about it?
Create each metric once, then record events through its correct operation: inc, set or observe.
Reuses named metrics and records counters, gauges, histograms and summaries. Legacy decorators require compatible TypeScript settings. Labels must have bounded cardinality; class decoration exposes a metric handle.
03 · Control flow
What happens in which order?
- Create or resolve the metric once.
- Wrap the target operation.
- Count a call or await its numeric result.
- Use the metric-specific recording method.
- Preserve original results and errors.
04 · Building blocks
The important contracts
PromCounter / PromGauge / PromHistogram / PromSummaryCreate reusable call or value instrumentation.TrackOption / IMetricConfigChoose semantics, labels and metric configuration.05 · Build it yourself
Implement a small core
Start with this small independent implementation, then use the steps below to make its contract explicit.
- Create or resolve the metric once.
- Wrap the target operation.
- Count a call or await its numeric result.
- Use the metric-specific recording method.
- Preserve original results and errors.
type Counter = { inc(labels?: Record<string, string>): void };
function countCalls<A extends unknown[], R>(
counter: Counter,
run: (...args: A) => R,
) {
return function (this: unknown, ...args: A): R {
counter.inc();
return run.apply(this, args);
};
}06 · Verify
What should you test?
- Invoke an instrumented method repeatedly without duplicate registration.
- Verify histogram and summary observe values.
- Keep property values independent between instances.
Try it yourself
Turn the first verification claim into a failing test, implement the smallest fix, then add a boundary case.
Invoke an instrumented method repeatedly without duplicate registration.
Compare your approach
Your check should fail when the stated invariant is deliberately broken, and pass again after the fix. Keep setup deterministic and assert an observable result.
07 · Limits
Trade-offs and failure modes
- Labels with unbounded values create excessive series.
- Metric recording must not change method return semantics.
- Decorator support depends on the configured TypeScript mode.
Legacy decorators require compatible TypeScript settings. Labels must have bounded cardinality; class decoration exposes a metric handle.
08 · Keep exploring
Source code and related ideas
yarn tsx packages/prom-metrics-decorator/examples/basic.ts