001package io.prometheus.client;
002
003import io.prometheus.client.CKMSQuantiles.Quantile;
004
005import java.io.Closeable;
006import java.util.ArrayList;
007import java.util.Arrays;
008import java.util.Collections;
009import java.util.List;
010import java.util.Map;
011import java.util.TreeMap;
012import java.util.concurrent.Callable;
013import java.util.concurrent.TimeUnit;
014
015/**
016 * Info metric, key-value pairs.
017 *
018 * Examples of Info include, build information, version information, and potential target metadata,
019 * The first provided state will be the default.
020 *
021 * <p>
022 * Example enumeration:
023 * <pre>
024 * {@code
025 *   class YourClass {
026 *     static final Info buildInfo = Info.build()
027 *         .name("your_build_info").help("Build information.")
028 *         .register();
029 *
030 *     void func() {
031 *          // Your code here.
032 *         buildInfo.info("branch", "HEAD", "version", "1.2.3", "revision", "e0704b");
033 *     }
034 *   }
035 * }
036 * </pre>
037 */
038public class Info extends SimpleCollector<Info.Child> implements Counter.Describable {
039
040  Info(Builder b) {
041    super(b);
042  }
043
044  public static class Builder extends SimpleCollector.Builder<Builder, Info> {
045    @Override
046    public Info create() {
047      if (!unit.isEmpty()) {
048        throw new IllegalStateException("Info metrics cannot have a unit.");
049      }
050      return new Info(this);
051    }
052  }
053
054  /**
055   *  Return a Builder to allow configuration of a new Info. Ensures required fields are provided.
056   *
057   *  @param name The name of the metric
058   *  @param help The help string of the metric
059   */
060  public static Builder build(String name, String help) {
061    return new Builder().name(name).help(help);
062  }
063
064  /**
065   *  Return a Builder to allow configuration of a new Info.
066   */
067  public static Builder build() {
068    return new Builder();
069  }
070
071  @Override
072  protected Child newChild() {
073    return new Child(labelNames);
074  }
075
076
077  /**
078   * The value of a single Info.
079   * <p>
080   * <em>Warning:</em> References to a Child become invalid after using
081   * {@link SimpleCollector#remove} or {@link SimpleCollector#clear}.
082   */
083  public static class Child {
084
085    private Map<String, String> value = Collections.emptyMap();
086    private List<String> labelNames;
087
088    private Child(List<String> labelNames) {
089      this.labelNames = labelNames;
090    }
091
092    /**
093     * Set the info.
094     */
095    public void info(Map<String, String> v) {
096      for (String key : v.keySet()) {
097        checkMetricLabelName(key);
098      }
099      for (String label : labelNames) {
100        if (v.containsKey(label)) {
101          throw new IllegalArgumentException("Info and its value cannot have the same label name.");
102        }
103      }
104      this.value = v;
105    }
106    /**
107     * Set the info.
108     *
109     * @param v labels as pairs of key values
110     */
111    public void info(String... v) {
112      if (v.length % 2 != 0) {
113        throw new IllegalArgumentException("An even number of arguments must be passed");
114      }
115      Map<String, String> m = new TreeMap<String, String>();
116      for (int i = 0; i < v.length; i+=2) {
117        m.put(v[i], v[i+1]);
118      }
119      info(m);
120    }
121
122    /**
123     * Get the info.
124     */
125    public Map<String, String> get() {
126      return value;
127    }
128  }
129
130  // Convenience methods.
131  /**
132   * Set the info on the info with no labels.
133   */
134  public void info(String... v) {
135    noLabelsChild.info(v);
136  }
137
138  /**
139   * Set the info on the info with no labels.
140   *
141   * @param v labels as pairs of key values
142   */
143  public void info(Map<String, String> v) {
144    noLabelsChild.info(v);
145  }
146
147  /**
148   * Get the the info.
149   */
150  public Map<String, String> get() {
151    return noLabelsChild.get();
152  }
153
154  @Override
155  public List<MetricFamilySamples> collect() {
156    List<MetricFamilySamples.Sample> samples = new ArrayList<MetricFamilySamples.Sample>();
157    for(Map.Entry<List<String>, Child> c: children.entrySet()) {
158      Map<String, String> v = c.getValue().get();
159      List<String> names = new ArrayList<String>(labelNames);
160      List<String> values = new ArrayList<String>(c.getKey());
161      for(Map.Entry<String, String> l: v.entrySet()) {
162        names.add(l.getKey());
163        values.add(l.getValue());
164      }
165      samples.add(new MetricFamilySamples.Sample(fullname + "_info", names, values, 1.0));
166    }
167
168    return familySamplesList(Type.INFO, samples);
169  }
170
171  @Override
172  public List<MetricFamilySamples> describe() {
173    return Collections.singletonList(
174            new MetricFamilySamples(fullname, Type.INFO, help, Collections.<MetricFamilySamples.Sample>emptyList()));
175  }
176
177}