3.10 The Metric Data Model

A metric is a quantity that varies and is worth watching over time: a utilisation, a queue depth, a running total, a distribution of latencies. Metrics are dense where events and logs are sparse — many measurements of the same thing rather than a record of a thing that happened — and the model reflects that.

A sample is one measurement, of one time series, at one moment. It carries:

  • a name, identifying what is measured
  • a label set, identifying which instance of it
  • a type, fixing how the value is to be read
  • a timestamp
  • a value, whose shape depends on the type

Name and labels together identify the series (§3.13). The type is a property of the series, not of the sample.

3.10.1 Names #

A name MUST match the identifier grammar of §3.19:

[A-Za-z_][A-Za-z0-9_.-]*

A collector MUST discard a record whose name does not, for the same two reasons an origin is constrained (§3.7): names are matched against patterns in which * is the wildcard, and names are what read access is granted on.

Beyond that, naming is convention and a collector MUST NOT enforce any. The conventions in use are a dot-separated hierarchy from general to specific, the unit as the last component, and a cumulative name for a cumulative quantity:

system.cpu.usage
disk.read.bytes
request.duration.seconds
http.requests.total

3.10.2 Labels #

Labels are the dimensions of a measurement: which core, which device, which method. cpu.usage with core="0" and cpu.usage with core="1" are two series, not two samples of one.

Label keys and values MUST be non-empty UTF-8 strings. A key MUST match the identifier grammar above; a value MUST NOT contain = (0x3D) or , (0x2C), which are reserved as delimiters in the collector's canonical representation of a label set (§3.13). A key MUST NOT be repeated within one sample, and MUST NOT be any of the five fixed field names a metric result carries — timestamp, boot_id, name, type, value — because labels and fixed fields share one flat namespace in a result record (§3.22) and a collision would make the record ambiguous.

A record violating any of these is discarded (§3.12).

Label cardinality is the producer's responsibility. Each distinct combination of label values is a distinct series, so labels whose values are unbounded — request identifiers, user-supplied strings, timestamps — produce series without limit, and a collector is required neither to cap them nor to degrade gracefully when they arrive. A producer SHOULD use labels whose value sets are small and known. A dimension that is not bounded belongs in an event payload, where it costs one field, not in a label, where it costs a series.

3.10.3 Types #

The type is fixed when the series is first seen and is immutable. A sample that resolves to an existing series but declares a different type is discarded (§3.12), permanently and without notification. A producer that changes the type of a metric it already emits has stopped emitting it, and the only visible symptom is that the series stops advancing.

3.10.3.1 Counter #

A value that only increases, and resets to zero when the producer restarts. Used for cumulative quantities: requests served, bytes transmitted, errors encountered.

A counter value MUST be a finite, non-negative binary64 value.

The raw value is rarely what a reader wants; the rate of change is (§3.25). A decrease is read as a restart rather than as a negative change, which is why the type must be declared: the same number sequence means something different for a gauge.

3.10.3.2 Gauge #

A value that may move in either direction. Used for current state: a utilisation, an amount in use, a depth, a temperature.

A gauge value MUST be a finite binary64 value, and MAY be negative.

3.10.3.3 Histogram #

A distribution of observations across buckets the producer chose. Used where the shape matters more than the mean — latencies above all.

A histogram value carries:

  • boundaries: a non-empty array of bucket upper bounds, strictly increasing in the order given
  • counts: one cumulative count per boundary, each being the number of observations less than or equal to that boundary; non-decreasing, and each no greater than the total
  • total_count: the number of observations
  • sum: the finite sum of the observations

Boundaries are part of the series identity (§3.13). A collector MUST NOT sort or reinterpret them; a producer that changes them has started a new series, and SHOULD therefore keep them fixed for the life of a metric.

The final count MAY be less than the total: observations above the highest boundary are the difference between them, and are not otherwise represented. A total of zero is a valid empty sample, in which case every count and the sum MUST be zero.

3.10.4 Values are floating point #

Numeric input MAY be a MessagePack integer or a MessagePack float; both are converted to binary64 with round-to-nearest, ties-to-even. Every value a collector stores and every value a query returns is a finite binary64.

Non-finite values are refused rather than stored: a record whose value converts to NaN or to either infinity is discarded (§3.12). There is no representation for a missing measurement — a producer with nothing to report sends nothing, and the gap is the answer (§3.13).

Edit this page