001package io.prometheus.metrics.core.metrics;
002
003import io.prometheus.metrics.config.PrometheusProperties;
004import io.prometheus.metrics.model.registry.Collector;
005import io.prometheus.metrics.model.registry.PrometheusRegistry;
006import io.prometheus.metrics.model.snapshots.Label;
007import io.prometheus.metrics.model.snapshots.Labels;
008import io.prometheus.metrics.model.snapshots.MetricSnapshot;
009
010import java.util.ArrayList;
011import java.util.List;
012
013/**
014 * Common base class for all metrics.
015 */
016public abstract class Metric implements Collector {
017
018    protected final Labels constLabels;
019
020    protected Metric(Builder<?, ?> builder) {
021        this.constLabels = builder.constLabels;
022    }
023
024    @Override
025    public abstract MetricSnapshot collect();
026
027    protected static abstract class Builder<B extends Builder<B, M>, M extends Metric> {
028
029        protected final List<String> illegalLabelNames;
030        protected final PrometheusProperties properties;
031        protected Labels constLabels = Labels.EMPTY;
032
033        protected Builder(List<String> illegalLabelNames, PrometheusProperties properties) {
034            this.illegalLabelNames = new ArrayList<>(illegalLabelNames);
035            this.properties = properties;
036        }
037
038        // ConstLabels are only used rarely. In particular, do not use them to
039        // attach the same labels to all your metrics. Those use cases are
040        // better covered by target labels set by the scraping Prometheus
041        // server, or by one specific metric (e.g. a build_info or a
042        // machine_role metric). See also
043        // https://prometheus.io/docs/instrumenting/writing_exporters/#target-labels-not-static-scraped-labels
044        public B constLabels(Labels constLabels) {
045            for (Label label : constLabels) { // NPE if constLabels is null
046                if (illegalLabelNames.contains(label.getName())) {
047                    throw new IllegalArgumentException(label.getName() + ": illegal label name for this metric type");
048                }
049            }
050            this.constLabels = constLabels;
051            return self();
052        }
053
054        public M register() {
055            return register(PrometheusRegistry.defaultRegistry);
056        }
057
058        public M register(PrometheusRegistry registry) {
059            M metric = build();
060            registry.register(metric);
061            return metric;
062        }
063
064        public abstract M build();
065
066        protected abstract B self();
067    }
068}