001
002package io.prometheus.client;
003
004import java.util.Arrays;
005import java.util.List;
006import java.util.regex.Pattern;
007
008/**
009 * A collector for a set of metrics.
010 * <p>
011 * Normal users should use {@link Gauge}, {@link Counter} and {@link Summary}.
012 * <p>
013 * Subclasssing Collector is for advanced uses, such as proxying metrics from another monitoring system.
014 * It is it the responsibility of subclasses to ensure they produce valid metrics.
015 * @see <a href="http://prometheus.io/docs/instrumenting/exposition_formats/">Exposition formats</a>.
016 */
017public abstract class Collector {
018  /**
019   * Return all of the metrics of this Collector.
020   */
021  public abstract List<MetricFamilySamples> collect();
022  public static enum Type {
023    COUNTER,
024    GAUGE,
025    SUMMARY,
026    HISTOGRAM,
027  }
028
029  /**
030   * A metric, and all of it's samples.
031   */
032  static public class MetricFamilySamples {
033    public final String name;
034    public final Type type;
035    public final String help;
036    public final List<Sample> samples;
037
038    public MetricFamilySamples(String name, Type type, String help, List<Sample> samples) {
039      this.name = name;
040      this.type = type;
041      this.help = help;
042      this.samples = samples;
043    }
044
045    @Override
046    public boolean equals(Object obj) {
047      if (obj == null || !(obj instanceof MetricFamilySamples)) {
048        return false;
049      }
050      MetricFamilySamples other = (MetricFamilySamples) obj;
051      
052      return other.name.equals(name) && other.type.equals(type)
053        && other.help.equals(help) && other.samples.equals(samples) ;
054    }
055
056    @Override
057    public int hashCode() {
058      int hash = 1;
059      hash = 37 * hash + name.hashCode();
060      hash = 37 * hash + type.hashCode();
061      hash = 37 * hash + help.hashCode();
062      hash = 37 * hash + samples.hashCode();
063      return hash;
064    }
065
066    @Override
067    public String toString() {
068      return "Name: " + name + " Type: " + type + " Help: " + help + 
069        " Samples: " + samples;
070    }
071
072  /**
073   * A single Sample, with a unique name and set of labels.
074   */
075    public static class Sample {
076      public final String name;
077      public final List<String> labelNames;
078      public final List<String> labelValues;  // Must have same length as labelNames.
079      public final double value;
080
081      public Sample(String name, List<String> labelNames, List<String> labelValues, double value) {
082        this.name = name;
083        this.labelNames = labelNames;
084        this.labelValues = labelValues;
085        this.value = value;
086      }
087
088      @Override
089      public boolean equals(Object obj) {
090        if (obj == null || !(obj instanceof Sample)) {
091          return false;
092        }
093        Sample other = (Sample) obj;
094        return other.name.equals(name) && other.labelNames.equals(labelNames)
095          && other.labelValues.equals(labelValues) && other.value == value;
096      }
097
098      @Override
099      public int hashCode() {
100        int hash = 1;
101        hash = 37 * hash + name.hashCode();
102        hash = 37 * hash + labelNames.hashCode();
103        hash = 37 * hash + labelValues.hashCode();
104        long d = Double.doubleToLongBits(value);
105        hash = 37 * hash + (int)(d ^ (d >>> 32));
106        return hash;
107      }
108
109      @Override
110      public String toString() {
111        return "Name: " + name + " LabelNames: " + Arrays.asList(labelNames) + " labelValues: " + labelValues + 
112          " Value: " + value;
113      }
114    }
115  }
116
117  /**
118   * Register the Collector with the default registry.
119   */
120  public <T extends Collector> T register() {
121    return register(CollectorRegistry.defaultRegistry);
122  }
123
124  /**
125   * Register the Collector with the given registry.
126   */
127  public <T extends Collector> T register(CollectorRegistry registry) {
128    registry.register(this);
129    return (T)this;
130  }
131
132  /* Various utility functions for implementing Collectors. */
133
134  /**
135   * Number of nanoseconds in a second.
136   */
137  public static final double NANOSECONDS_PER_SECOND = 1E9;
138  /**
139   * Number of milliseconds in a second.
140   */
141  public static final double MILLISECONDS_PER_SECOND = 1E3;
142
143  protected static final Pattern METRIC_NAME_RE = Pattern.compile("[a-zA-Z_:][a-zA-Z0-9_:]*");
144  protected static final Pattern METRIC_LABEL_NAME_RE = Pattern.compile("[a-zA-Z_][a-zA-Z0-9_]*");
145  protected static final Pattern RESERVED_METRIC_LABEL_NAME_RE = Pattern.compile("__.*");
146
147  /**
148   * Throw an exception if the metric name is invalid.
149   */
150  protected static void checkMetricName(String name) {
151    if (!METRIC_NAME_RE.matcher(name).matches()) {
152      throw new IllegalArgumentException("Invalid metric name: " + name);
153    }
154  }
155
156  /**
157   * Throw an exception if the metric label name is invalid.
158   */
159  protected static void checkMetricLabelName(String name) {
160    if (!METRIC_LABEL_NAME_RE.matcher(name).matches()) {
161      throw new IllegalArgumentException("Invalid metric label name: " + name);
162    }
163    if (RESERVED_METRIC_LABEL_NAME_RE.matcher(name).matches()) {
164      throw new IllegalArgumentException("Invalid metric label name, reserved for internal use: " + name);
165    }
166  }
167
168  /**
169   * Convert a double to it's string representation in Go.
170   */
171  public static String doubleToGoString(double d) {
172    if (d == Double.POSITIVE_INFINITY) {
173      return "+Inf";
174    } 
175    if (d == Double.NEGATIVE_INFINITY) {
176      return "-Inf";
177    }
178    return Double.toString(d);
179  }
180}