001package io.prometheus.metrics.core.metrics; 002 003import io.prometheus.metrics.config.PrometheusProperties; 004import io.prometheus.metrics.model.snapshots.Labels; 005import io.prometheus.metrics.model.snapshots.MetricMetadata; 006import io.prometheus.metrics.model.snapshots.PrometheusNaming; 007import io.prometheus.metrics.model.snapshots.Unit; 008 009import java.util.Arrays; 010import java.util.List; 011 012/** 013 * Almost all metrics have fixed metadata, i.e. the metric name is known when the metric is created. 014 * <p> 015 * An exception would be a metric that is a bridge to a 3rd party metric library, where the metric name 016 * has to be retrieved from the 3rd party metric library at scrape time. 017 */ 018public abstract class MetricWithFixedMetadata extends Metric { 019 020 private final MetricMetadata metadata; 021 protected final String[] labelNames; 022 023 protected MetricWithFixedMetadata(Builder<?, ?> builder) { 024 super(builder); 025 this.metadata = new MetricMetadata(makeName(builder.name, builder.unit), builder.help, builder.unit); 026 this.labelNames = Arrays.copyOf(builder.labelNames, builder.labelNames.length); 027 } 028 029 protected MetricMetadata getMetadata() { 030 return metadata; 031 } 032 033 private String makeName(String name, Unit unit) { 034 if (unit != null) { 035 if (!name.endsWith(unit.toString())) { 036 name = name + "_" + unit; 037 } 038 } 039 return name; 040 } 041 042 @Override 043 public String getPrometheusName() { 044 return metadata.getPrometheusName(); 045 } 046 047 public static abstract class Builder<B extends Builder<B, M>, M extends MetricWithFixedMetadata> extends Metric.Builder<B, M> { 048 049 protected String name; 050 private Unit unit; 051 private String help; 052 private String[] labelNames = new String[0]; 053 054 protected Builder(List<String> illegalLabelNames, PrometheusProperties properties) { 055 super(illegalLabelNames, properties); 056 } 057 058 public B name(String name) { 059 if (!PrometheusNaming.isValidMetricName(name)) { 060 throw new IllegalArgumentException("'" + name + "': Illegal metric name."); 061 } 062 this.name = name; 063 return self(); 064 } 065 066 public B unit(Unit unit) { 067 this.unit = unit; 068 return self(); 069 } 070 071 public B help(String help) { 072 this.help = help; 073 return self(); 074 } 075 076 public B labelNames(String... labelNames) { 077 for (String labelName : labelNames) { 078 if (!PrometheusNaming.isValidLabelName(labelName)) { 079 throw new IllegalArgumentException(labelName + ": illegal label name"); 080 } 081 if (illegalLabelNames.contains(labelName)) { 082 throw new IllegalArgumentException(labelName + ": illegal label name for this metric type"); 083 } 084 if (constLabels.contains(labelName)) { 085 throw new IllegalArgumentException(labelName + ": duplicate label name"); 086 } 087 } 088 this.labelNames = labelNames; 089 return self(); 090 } 091 092 public B constLabels(Labels constLabels) { 093 for (String labelName : labelNames) { 094 if (constLabels.contains(labelName)) { // Labels.contains() treats dots like underscores 095 throw new IllegalArgumentException(labelName + ": duplicate label name"); 096 } 097 } 098 return super.constLabels(constLabels); 099 } 100 101 @Override 102 public abstract M build(); 103 104 @Override 105 protected abstract B self(); 106 } 107}