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