Problem
Ad hoc metrics have inconsistent names, labels and units, so dashboards cannot be reused and queries behave unpredictably. Careless labels create cardinality explosions that crash monitoring backends and inflate cost.
Instrument services consistently with metrics that answer operational questions at controlled cost.
Ad hoc metrics have inconsistent names, labels and units, so dashboards cannot be reused and queries behave unpredictably. Careless labels create cardinality explosions that crash monitoring backends and inflate cost.
Good metrics are cheap to store, cheap to query and answer a named question. This guide sets conventions so each service emits comparable signals with bounded cost.
Every request-handling service exposes rate, errors and duration. Every resource such as a queue or pool exposes utilisation, saturation and errors. Metric types are chosen deliberately: counters for totals that only increase, gauges for values that go up and down, and histograms for distributions. A shared library supplies middleware that records the standard signals so teams add business metrics rather than reinvent plumbing.
Instrumentation should start from questions rather than from available data. For a new service, ask what a responder needs to know in the first five minutes of an incident, which user journeys matter most, and which dependencies could fail. Then ensure each question maps to a metric or a query. This question-first approach avoids both the empty dashboard and the thousand-panel dashboard, and it makes review easier because every metric has a stated purpose that can be checked later.
from prometheus_client import Counter, Histogram
REQUESTS = Counter(
"checkout_http_requests_total",
"Total HTTP requests",
["method", "route", "status_class"],
)
DURATION = Histogram(
"checkout_http_request_duration_seconds",
"Request latency in seconds",
["method", "route"],
buckets=(0.05, 0.1, 0.2, 0.3, 0.5, 1.0, 2.5),
)
def observe(method, route, status, seconds):
REQUESTS.labels(method, route, f"{status // 100}xx").inc()
DURATION.labels(method, route).observe(seconds)