Skip to content

Commit 992d83d

Browse files
committed
docs: cover typed family descriptors and @stableAPI
Update registry and model docs to point custom collectors at getMetricFamilyDescriptor(), noting the deprecated fragmented metadata methods bridge for compatibility. Add an internals/stability page covering @stableAPI, the mise run api-diff baseline check, and the breaking-api-change-accepted label. Signed-off-by: Gregor Zeitlinger <gregor.zeitlinger@grafana.com>
1 parent 82d3c46 commit 992d83d

3 files changed

Lines changed: 70 additions & 20 deletions

File tree

docs/content/getting-started/registry.md

Lines changed: 30 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -104,25 +104,39 @@ Validation of duplicate metric names and label schemas happens at registration t
104104
Built-in metrics (Counter, Gauge, Histogram, etc.) participate in this validation.
105105

106106
Custom collectors that implement the `Collector` or `MultiCollector` interface can optionally
107-
implement `getPrometheusName()` and `getMetricType()` (and the MultiCollector per-name variants) so
108-
the registry can enforce consistency. **Validation is skipped when metric name or type is
109-
unavailable:** if `getPrometheusName()` or `getMetricType()` returns `null`, the registry does not
110-
validate that collector. If two such collectors produce the same metric name and same label set at
111-
scrape time, the exposition output may contain duplicate time series and be invalid for Prometheus.
112-
113-
When validation _is_ performed (name and type are non-null), **null label names are treated as an
114-
empty label schema:** `getLabelNames()` returning `null` is normalized to `Collections.emptySet()`
115-
and full label-schema validation and duplicate detection still apply. A collector that returns a
116-
non-null type but leaves `getLabelNames()` as `null` is still validated, with its labels treated as
117-
empty.
107+
expose their registration-time metadata so the registry can enforce consistency. The recommended
108+
way is to override `getMetricFamilyDescriptor()` (or `getMetricFamilyDescriptors()` on
109+
`MultiCollector`) and return a `MetricFamilyDescriptor` describing the metric name, type, label
110+
names, and metadata (help, unit) the collector will emit at scrape time.
111+
112+
```java
113+
@Override
114+
public MetricFamilyDescriptor getMetricFamilyDescriptor() {
115+
return MetricFamilyDescriptor.gauge("my_metric")
116+
.help("Example metric")
117+
.labelNames("region")
118+
.build();
119+
}
120+
```
121+
122+
The fragmented `getPrometheusName()`, `getMetricType()`, `getLabelNames()`, and `getMetadata()`
123+
methods (and their `MultiCollector` per-name variants) are deprecated. They remain bridged by a
124+
default implementation of `getMetricFamilyDescriptor()` for compatibility, so existing
125+
collectors keep working unchanged.
126+
127+
**Validation is skipped when registration-time metadata is unavailable:** if
128+
`getMetricFamilyDescriptor()` returns `null` (the default when name or type is missing), the
129+
registry does not validate that collector. If two such collectors produce the same metric name and
130+
same label set at scrape time, the exposition output may contain duplicate time series and be
131+
invalid for Prometheus.
118132

119133
This is also relevant for downstream adapter libraries that bridge to this registry. If an adapter
120134
implements `MultiCollector`, its registration-time metadata must match the metric families it will
121-
actually emit at scrape time. In practice, that means `getPrometheusNames()`, `getMetricType(...)`,
122-
`getLabelNames(...)`, and `getMetadata(...)` need to describe the same names, types, labels, and
123-
suffix behavior as the eventual `MetricSnapshot` output. Otherwise an adapter may pass or fail
124-
collision checks differently after upgrading to a newer client_java release, even if its scrape
125-
output logic did not change.
135+
actually emit at scrape time. In practice, the `MetricFamilyDescriptor`s returned from
136+
`getMetricFamilyDescriptors()` need to describe the same names, types, labels, and suffix behavior
137+
as the eventual `MetricSnapshot` output. Otherwise an adapter may pass or fail collision checks
138+
differently after upgrading to a newer client_java release, even if its scrape output logic did not
139+
change.
126140

127141
## Unregistering a Metric
128142

docs/content/internals/model.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,10 +19,10 @@ All metric types implement
1919
the [Collector](/client_java/api/io/prometheus/metrics/model/registry/Collector.html) interface,
2020
i.e. they provide
2121
a [collect()](</client_java/api/io/prometheus/metrics/model/registry/Collector.html#collect()>)
22-
method to produce snapshots. Implementers that do not provide metric type or label names (returning
23-
null from `getMetricType()` and `getLabelNames()`) are not validated at registration; they must
24-
avoid producing the same metric name and label schema as another collector, or exposition may be
25-
invalid.
22+
method to produce snapshots. Implementers expose their registration-time metadata via
23+
`getMetricFamilyDescriptor()` (or `getMetricFamilyDescriptors()` on `MultiCollector`). When that
24+
returns `null`, the collector is not validated at registration and must avoid producing the same
25+
metric name and label schema as another collector, or exposition may be invalid.
2626

2727
## prometheus-metrics-model
2828

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
---
2+
title: API stability
3+
weight: 2
4+
---
5+
6+
The published Java API surface is marked with the
7+
[`@StableApi`](/client_java/api/io/prometheus/metrics/annotations/StableApi.html) annotation. The
8+
annotation is opt-in: only annotated types and members are part of the stable, published API and
9+
follow semantic versioning — backwards-incompatible changes happen only in a major version bump.
10+
Unannotated public types are not part of the stability contract and may change in any release.
11+
12+
`@StableApi` can be applied to a type to publish the type and its members, or to individual
13+
constructors, methods, and fields when only part of a public type is stable.
14+
15+
## API diff check
16+
17+
CI runs [japicmp](https://siom79.github.io/japicmp/) against a pinned baseline release and fails on
18+
incompatible changes to the `@StableApi` surface. Run it locally with:
19+
20+
```bash
21+
mise run api-diff
22+
```
23+
24+
Reports are written to `**/target/japicmp/*`.
25+
26+
The baseline version is tracked in `pom.xml` and updated by Renovate; the published baseline diffs
27+
are stored under `docs/apidiffs/`.
28+
29+
## Accepting breaking changes
30+
31+
Backwards-incompatible changes to the `@StableApi` surface are only allowed in a major version
32+
bump. Within a major version line, the API diff check must pass.
33+
34+
When a major version is being prepared, intentional incompatible changes can be accepted by adding
35+
the `breaking-api-change-accepted` label to the pull request. The label is not an escape hatch for
36+
minor or patch releases.

0 commit comments

Comments
 (0)