001package io.prometheus.metrics.model.registry;
002
003import io.prometheus.metrics.annotations.StableApi;
004import io.prometheus.metrics.model.snapshots.MetricFamilyDescriptor;
005import io.prometheus.metrics.model.snapshots.MetricMetadata;
006import io.prometheus.metrics.model.snapshots.MetricSnapshot;
007import io.prometheus.metrics.model.snapshots.MetricSnapshots;
008import java.util.ArrayList;
009import java.util.Collections;
010import java.util.List;
011import java.util.Set;
012import java.util.function.Predicate;
013import javax.annotation.Nullable;
014
015/** Like {@link Collector}, but collecting multiple Snapshots at once. */
016@FunctionalInterface
017@StableApi
018public interface MultiCollector {
019
020  /** Called when the Prometheus server scrapes metrics. */
021  MetricSnapshots collect();
022
023  /**
024   * Provides Collector with the details of the request issued by Prometheus to allow multi-target
025   * pattern implementation Override to implement request dependent logic to provide MetricSnapshot
026   */
027  default MetricSnapshots collect(PrometheusScrapeRequest scrapeRequest) {
028    return collect();
029  }
030
031  /**
032   * Like {@link #collect()}, but returns only the snapshots where {@code includedNames.test(name)}
033   * is {@code true}.
034   *
035   * <p>Override this if there is a more efficient way than first collecting all snapshot and then
036   * discarding the excluded ones.
037   */
038  default MetricSnapshots collect(Predicate<String> includedNames) {
039    return collect(includedNames, null);
040  }
041
042  /**
043   * Like {@link #collect(Predicate)}, but with support for multi-target pattern.
044   *
045   * <p>Override this if there is a more efficient way than first collecting the snapshot and then
046   * discarding it.
047   */
048  default MetricSnapshots collect(
049      Predicate<String> includedNames, @Nullable PrometheusScrapeRequest scrapeRequest) {
050    MetricSnapshots allSnapshots = scrapeRequest == null ? collect() : collect(scrapeRequest);
051    MetricSnapshots.Builder result = MetricSnapshots.builder();
052    for (MetricSnapshot snapshot : allSnapshots) {
053      if (includedNames.test(snapshot.getMetadata().getPrometheusName())) {
054        result.metricSnapshot(snapshot);
055      }
056    }
057    return result.build();
058  }
059
060  /**
061   * Returns registration-time descriptors for the metric families collected by this collector.
062   *
063   * <p>The registry uses these descriptors for duplicate-name, type, label-schema, help, and unit
064   * validation at registration time. Returning an empty list means registration-time validation is
065   * skipped for this collector.
066   *
067   * <p>The default implementation adapts the deprecated fragmented metadata methods. New collectors
068   * with fixed registration-time metadata should override this method directly.
069   */
070  @SuppressWarnings("deprecation")
071  default List<MetricFamilyDescriptor> getMetricFamilyDescriptors() {
072    List<String> prometheusNames = getPrometheusNames();
073    List<MetricFamilyDescriptor> descriptors = new ArrayList<>(prometheusNames.size());
074    for (String prometheusName : prometheusNames) {
075      MetricType metricType = getMetricType(prometheusName);
076      if (metricType != null) {
077        MetricMetadata metadata = getMetadata(prometheusName);
078        if (metadata == null) {
079          metadata = new MetricMetadata(prometheusName);
080        }
081        Set<String> labelNames = getLabelNames(prometheusName);
082        descriptors.add(
083            MetricFamilyDescriptor.of(
084                metricType, metadata, labelNames == null ? Collections.emptySet() : labelNames));
085      }
086    }
087    return Collections.unmodifiableList(descriptors);
088  }
089
090  /**
091   * This is called in two places:
092   *
093   * <ol>
094   *   <li>During registration to check if a metric with that name already exists.
095   *   <li>During scrape to check if the collector can be skipped because a name filter is present
096   *       and all names are excluded.
097   * </ol>
098   *
099   * <p>Returning an empty list means checks are omitted (registration metric always succeeds), and
100   * the collector is always scraped (if a name filter is present and all names are excluded the
101   * result is dropped).
102   *
103   * <p>If your collector returns a constant list of metrics that have names that do not change at
104   * runtime it is a good idea to overwrite this and return the names.
105   *
106   * @deprecated Override {@link #getMetricFamilyDescriptors()} instead.
107   */
108  @Deprecated
109  default List<String> getPrometheusNames() {
110    return Collections.emptyList();
111  }
112
113  /**
114   * Returns the metric type for the given Prometheus name.
115   *
116   * <p>This is used for per-name type validation during registration. Returning {@code null} means
117   * type validation is skipped for that specific metric name.
118   *
119   * <p>Validation is performed only at registration time. If this method returns {@code null}, no
120   * type validation is performed for that name, and duplicate or conflicting metrics may result in
121   * invalid exposition output.
122   *
123   * @param prometheusName the Prometheus metric name
124   * @return the metric type for the given name, or {@code null} to skip validation
125   * @deprecated Override {@link #getMetricFamilyDescriptors()} instead.
126   */
127  @Deprecated
128  @Nullable
129  default MetricType getMetricType(String prometheusName) {
130    return null;
131  }
132
133  /**
134   * Returns the complete set of label names for the given Prometheus name.
135   *
136   * <p>This includes both dynamic label names and constant label names. Label names are normalized
137   * using Prometheus naming conventions (dots converted to underscores).
138   *
139   * <p>This is used for per-name label schema validation during registration. Two collectors with
140   * the same name and type can coexist if they have different label name sets.
141   *
142   * <p>Returning {@code null} is treated as an empty label set: the registry normalizes it to
143   * {@code Collections.emptySet()} and performs full label-schema validation and duplicate
144   * detection. Two collectors with the same name, type, and {@code null} (or empty) label names are
145   * considered duplicate and registration of the second will fail.
146   *
147   * @param prometheusName the Prometheus metric name
148   * @return the set of all label names for the given name, or {@code null} (treated as empty) for a
149   *     metric with no labels
150   * @deprecated Override {@link #getMetricFamilyDescriptors()} instead.
151   */
152  @Deprecated
153  @Nullable
154  default Set<String> getLabelNames(String prometheusName) {
155    return null;
156  }
157
158  /**
159   * Returns the metric metadata (name, help, unit) for the given Prometheus name.
160   *
161   * <p>When non-null, the registry uses this to validate that metrics with the same name have
162   * consistent help and unit. Returning {@code null} means help/unit validation is skipped for that
163   * name.
164   *
165   * @param prometheusName the Prometheus metric name
166   * @return the metric metadata for that name, or {@code null} to skip help/unit validation
167   * @deprecated Override {@link #getMetricFamilyDescriptors()} instead.
168   */
169  @Deprecated
170  @Nullable
171  default MetricMetadata getMetadata(String prometheusName) {
172    return null;
173  }
174}