Metrics
Metric Types: Counters, Gauges, Histograms & Summaries
The four core metric types, when to use each, and why histograms and summaries handle distributions so differently.
Last updated
Every metrics system built around Prometheus-style instrumentation (and most others, in spirit) exposes the same four fundamental metric types. Picking the right one is a small decision that has outsized consequences for what questions you can later ask of the data.
The four types
- Counter. A cumulative value that only increases (or resets to zero on restart) — total requests served, total errors raised, total bytes sent. You never decrement a counter; if a value can go down, it isn’t a counter. Rate of change (
rate()in PromQL) is the standard way to make a counter useful, turning “total requests since start” into “requests per second right now.” - Gauge. A value that can go up or down arbitrarily — current memory usage, number of active connections, queue depth, temperature. Gauges represent a snapshot at the moment of collection, not an accumulation.
- Histogram. Samples observations (typically request durations or response sizes) into configurable buckets, and exposes a count and sum alongside per-bucket counts. Quantiles are computed after the fact, at query time, by interpolating across bucket boundaries — in Prometheus this is the
histogram_quantile()function. - Summary. Also samples observations, but calculates configured quantiles (e.g. p50, p95, p99) directly on the client, over a sliding time window, and exposes those precomputed quantile values.
Why it matters
- Counters and gauges answer fundamentally different questions. A counter answers “how much total activity has happened,” which is only meaningful as a rate of change; a gauge answers “what is the value right now.” Using a gauge where a counter belongs (or vice versa) breaks the standard query patterns built around each type and confuses anyone reading the dashboard later.
- Histograms aggregate across instances; summaries generally don’t. Because a histogram exposes raw bucket counts, you can sum buckets from many instances and compute a correct overall quantile server-side. A summary’s quantiles are already computed per instance over its own window, so averaging or summing quantiles across instances produces a mathematically meaningless number — you cannot average percentiles.
- The quantile is only as good as the bucket boundaries or algorithm you chose. A classic histogram’s accuracy is capped by how well its fixed bucket boundaries fit the actual data distribution — a p99 that falls between two widely spaced buckets is only a rough interpolation. Summaries, by computing quantiles from raw samples, are more accurate in isolation but lock in the quantiles and window at instrumentation time.
- This choice affects your SLOs. Service level objectives are usually built on latency percentiles; picking a metric type that can’t be aggregated the way your query layer needs (e.g. per-service across many pods) will quietly produce wrong numbers that only surface when someone tries to change the aggregation later.
Histogram vs. summary in practice
- Client-side vs. server-side computation. Summaries compute exact quantiles on the client over a sliding window and ship those numbers directly; histograms ship raw bucket counts and defer quantile computation to query time on the server.
- Flexibility after the fact. With a histogram, changing which percentile you care about, or the time window you look at, is just a different query — no redeploy needed. With a summary, changing the tracked quantiles means changing the instrumentation code and redeploying.
- Aggregatability is the deciding factor for most teams. Because modern systems run many replicas of the same service, and dashboards need a single p99 across the whole fleet, histograms are the default choice in most Prometheus-based setups today; summaries are reserved for cases needing a precise quantile on a single instance where cross-instance aggregation isn’t needed.
- Native histograms narrow the trade-off. Newer histogram implementations (Prometheus’s native histograms) use dynamically sized, exponential buckets that give much higher resolution at a fraction of the storage and cardinality cost of classic fixed-bucket histograms, while remaining aggregatable even if resolution differs between sources.
Common mistakes
- Using a gauge to track something that only accumulates, like total requests, which prevents using
rate()and makes it easy to double-count or miscount on scrape gaps. - Choosing bucket boundaries that don’t match the real latency distribution — e.g. buckets at 100ms, 500ms, 1s for a service that mostly responds in 2-10ms — which makes
histogram_quantile()output nearly useless. - Averaging percentiles from a summary across multiple instances, producing a number that looks plausible but has no valid statistical meaning.
- Instrumenting a histogram with far too many buckets or labels, which multiplies cardinality — a histogram already produces multiple series per label combination (one per bucket, plus
_sumand_count), so it’s especially sensitive to the cardinality problems described elsewhere in this hub.