001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements.  See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership.  The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019package org.apache.hadoop.metrics2.lib;
020
021import java.util.Collection;
022import java.util.Map;
023
024import com.google.common.collect.Maps;
025import com.google.common.base.Objects;
026
027import org.apache.hadoop.classification.InterfaceAudience;
028import org.apache.hadoop.classification.InterfaceStability;
029import org.apache.hadoop.metrics2.MetricsInfo;
030import org.apache.hadoop.metrics2.MetricsException;
031import org.apache.hadoop.metrics2.MetricsRecordBuilder;
032import org.apache.hadoop.metrics2.MetricsTag;
033import org.apache.hadoop.metrics2.impl.MsInfo;
034
035/**
036 * An optional metrics registry class for creating and maintaining a
037 * collection of MetricsMutables, making writing metrics source easier.
038 */
039@InterfaceAudience.Public
040@InterfaceStability.Evolving
041public class MetricsRegistry {
042  private final Map<String, MutableMetric> metricsMap = Maps.newLinkedHashMap();
043  private final Map<String, MetricsTag> tagsMap = Maps.newLinkedHashMap();
044  private final MetricsInfo metricsInfo;
045
046  /**
047   * Construct the registry with a record name
048   * @param name  of the record of the metrics
049   */
050  public MetricsRegistry(String name) {
051    metricsInfo = Interns.info(name, name);
052  }
053
054  /**
055   * Construct the registry with a metadata object
056   * @param info  the info object for the metrics record/group
057   */
058  public MetricsRegistry(MetricsInfo info) {
059    metricsInfo = info;
060  }
061
062  /**
063   * @return the info object of the metrics registry
064   */
065  public MetricsInfo info() {
066    return metricsInfo;
067  }
068
069  /**
070   * Get a metric by name
071   * @param name  of the metric
072   * @return the metric object
073   */
074  public synchronized MutableMetric get(String name) {
075    return metricsMap.get(name);
076  }
077
078  /**
079   * Get a tag by name
080   * @param name  of the tag
081   * @return the tag object
082   */
083  public synchronized MetricsTag getTag(String name) {
084    return tagsMap.get(name);
085  }
086
087  /**
088   * Create a mutable integer counter
089   * @param name  of the metric
090   * @param desc  metric description
091   * @param iVal  initial value
092   * @return a new counter object
093   */
094  public MutableCounterInt newCounter(String name, String desc, int iVal) {
095    return newCounter(Interns.info(name, desc), iVal);
096  }
097
098  /**
099   * Create a mutable integer counter
100   * @param info  metadata of the metric
101   * @param iVal  initial value
102   * @return a new counter object
103   */
104  public synchronized MutableCounterInt newCounter(MetricsInfo info, int iVal) {
105    checkMetricName(info.name());
106    MutableCounterInt ret = new MutableCounterInt(info, iVal);
107    metricsMap.put(info.name(), ret);
108    return ret;
109  }
110
111  /**
112   * Create a mutable long integer counter
113   * @param name  of the metric
114   * @param desc  metric description
115   * @param iVal  initial value
116   * @return a new counter object
117   */
118  public MutableCounterLong newCounter(String name, String desc, long iVal) {
119    return newCounter(Interns.info(name, desc), iVal);
120  }
121
122  /**
123   * Create a mutable long integer counter
124   * @param info  metadata of the metric
125   * @param iVal  initial value
126   * @return a new counter object
127   */
128  public synchronized
129  MutableCounterLong newCounter(MetricsInfo info, long iVal) {
130    checkMetricName(info.name());
131    MutableCounterLong ret = new MutableCounterLong(info, iVal);
132    metricsMap.put(info.name(), ret);
133    return ret;
134  }
135
136  /**
137   * Create a mutable integer gauge
138   * @param name  of the metric
139   * @param desc  metric description
140   * @param iVal  initial value
141   * @return a new gauge object
142   */
143  public MutableGaugeInt newGauge(String name, String desc, int iVal) {
144    return newGauge(Interns.info(name, desc), iVal);
145  }
146  /**
147   * Create a mutable integer gauge
148   * @param info  metadata of the metric
149   * @param iVal  initial value
150   * @return a new gauge object
151   */
152  public synchronized MutableGaugeInt newGauge(MetricsInfo info, int iVal) {
153    checkMetricName(info.name());
154    MutableGaugeInt ret = new MutableGaugeInt(info, iVal);
155    metricsMap.put(info.name(), ret);
156    return ret;
157  }
158
159  /**
160   * Create a mutable long integer gauge
161   * @param name  of the metric
162   * @param desc  metric description
163   * @param iVal  initial value
164   * @return a new gauge object
165   */
166  public MutableGaugeLong newGauge(String name, String desc, long iVal) {
167    return newGauge(Interns.info(name, desc), iVal);
168  }
169
170  /**
171   * Create a mutable long integer gauge
172   * @param info  metadata of the metric
173   * @param iVal  initial value
174   * @return a new gauge object
175   */
176  public synchronized MutableGaugeLong newGauge(MetricsInfo info, long iVal) {
177    checkMetricName(info.name());
178    MutableGaugeLong ret = new MutableGaugeLong(info, iVal);
179    metricsMap.put(info.name(), ret);
180    return ret;
181  }
182
183  /**
184   * Create a mutable double gauge
185   * @param name  of the metric
186   * @param desc  metric description
187   * @param val  initial value
188   * @return a new gauge object
189   */
190  public MutableGaugeDouble newGauge(String name, String desc, double val) {
191    return newGauge(Interns.info(name, desc), val);
192  }
193
194  /**
195   * Create a mutable double gauge
196   * @param info  metadata of the metric
197   * @param val  initial value
198   * @return a new gauge object
199   */
200  public synchronized MutableGaugeDouble newGauge(MetricsInfo info, double val) {
201    checkMetricName(info.name());
202    MutableGaugeDouble ret = new MutableGaugeDouble(info, val);
203    metricsMap.put(info.name(), ret);
204    return ret;
205  }
206
207  /**
208   * Create a mutable metric that estimates quantiles of a stream of values
209   * @param name of the metric
210   * @param desc metric description
211   * @param sampleName of the metric (e.g., "Ops")
212   * @param valueName of the metric (e.g., "Time" or "Latency")
213   * @param interval rollover interval of estimator in seconds
214   * @return a new quantile estimator object
215   */
216  public synchronized MutableQuantiles newQuantiles(String name, String desc,
217      String sampleName, String valueName, int interval) {
218    checkMetricName(name);
219    MutableQuantiles ret = 
220        new MutableQuantiles(name, desc, sampleName, valueName, interval);
221    metricsMap.put(name, ret);
222    return ret;
223  }
224  
225  /**
226   * Create a mutable metric with stats
227   * @param name  of the metric
228   * @param desc  metric description
229   * @param sampleName  of the metric (e.g., "Ops")
230   * @param valueName   of the metric (e.g., "Time" or "Latency")
231   * @param extended    produce extended stat (stdev, min/max etc.) if true.
232   * @return a new mutable stat metric object
233   */
234  public synchronized MutableStat newStat(String name, String desc,
235      String sampleName, String valueName, boolean extended) {
236    checkMetricName(name);
237    MutableStat ret =
238        new MutableStat(name, desc, sampleName, valueName, extended);
239    metricsMap.put(name, ret);
240    return ret;
241  }
242
243  /**
244   * Create a mutable metric with stats
245   * @param name  of the metric
246   * @param desc  metric description
247   * @param sampleName  of the metric (e.g., "Ops")
248   * @param valueName   of the metric (e.g., "Time" or "Latency")
249   * @return a new mutable metric object
250   */
251  public MutableStat newStat(String name, String desc,
252                             String sampleName, String valueName) {
253    return newStat(name, desc, sampleName, valueName, false);
254  }
255
256  /**
257   * Create a mutable rate metric
258   * @param name  of the metric
259   * @return a new mutable metric object
260   */
261  public MutableRate newRate(String name) {
262    return newRate(name, name, false);
263  }
264
265  /**
266   * Create a mutable rate metric
267   * @param name  of the metric
268   * @param description of the metric
269   * @return a new mutable rate metric object
270   */
271  public MutableRate newRate(String name, String description) {
272    return newRate(name, description, false);
273  }
274
275  /**
276   * Create a mutable rate metric (for throughput measurement)
277   * @param name  of the metric
278   * @param desc  description
279   * @param extended  produce extended stat (stdev/min/max etc.) if true
280   * @return a new mutable rate metric object
281   */
282  public MutableRate newRate(String name, String desc, boolean extended) {
283    return newRate(name, desc, extended, true);
284  }
285
286  @InterfaceAudience.Private
287  public synchronized MutableRate newRate(String name, String desc,
288      boolean extended, boolean returnExisting) {
289    if (returnExisting) {
290      MutableMetric rate = metricsMap.get(name);
291      if (rate != null) {
292        if (rate instanceof MutableRate) return (MutableRate) rate;
293        throw new MetricsException("Unexpected metrics type "+ rate.getClass()
294                                   +" for "+ name);
295      }
296    }
297    checkMetricName(name);
298    MutableRate ret = new MutableRate(name, desc, extended);
299    metricsMap.put(name, ret);
300    return ret;
301  }
302
303  synchronized void add(String name, MutableMetric metric) {
304    checkMetricName(name);
305    metricsMap.put(name, metric);
306  }
307
308  /**
309   * Add sample to a stat metric by name.
310   * @param name  of the metric
311   * @param value of the snapshot to add
312   */
313  public synchronized void add(String name, long value) {
314    MutableMetric m = metricsMap.get(name);
315
316    if (m != null) {
317      if (m instanceof MutableStat) {
318        ((MutableStat) m).add(value);
319      }
320      else {
321        throw new MetricsException("Unsupported add(value) for metric "+ name);
322      }
323    }
324    else {
325      metricsMap.put(name, newRate(name)); // default is a rate metric
326      add(name, value);
327    }
328  }
329
330  /**
331   * Set the metrics context tag
332   * @param name of the context
333   * @return the registry itself as a convenience
334   */
335  public MetricsRegistry setContext(String name) {
336    return tag(MsInfo.Context, name, true);
337  }
338
339  /**
340   * Add a tag to the metrics
341   * @param name  of the tag
342   * @param description of the tag
343   * @param value of the tag
344   * @return the registry (for keep adding tags)
345   */
346  public MetricsRegistry tag(String name, String description, String value) {
347    return tag(name, description, value, false);
348  }
349
350  /**
351   * Add a tag to the metrics
352   * @param name  of the tag
353   * @param description of the tag
354   * @param value of the tag
355   * @param override  existing tag if true
356   * @return the registry (for keep adding tags)
357   */
358  public MetricsRegistry tag(String name, String description, String value,
359                             boolean override) {
360    return tag(Interns.info(name, description), value, override);
361  }
362
363  /**
364   * Add a tag to the metrics
365   * @param info  metadata of the tag
366   * @param value of the tag
367   * @param override existing tag if true
368   * @return the registry (for keep adding tags etc.)
369   */
370  public synchronized
371  MetricsRegistry tag(MetricsInfo info, String value, boolean override) {
372    if (!override) checkTagName(info.name());
373    tagsMap.put(info.name(), Interns.tag(info, value));
374    return this;
375  }
376
377  public MetricsRegistry tag(MetricsInfo info, String value) {
378    return tag(info, value, false);
379  }
380
381  Collection<MetricsTag> tags() {
382    return tagsMap.values();
383  }
384
385  Collection<MutableMetric> metrics() {
386    return metricsMap.values();
387  }
388
389  private void checkMetricName(String name) {
390    // Check for invalid characters in metric name
391    boolean foundWhitespace = false;
392    for (int i = 0; i < name.length(); i++) {
393      char c = name.charAt(i);
394      if (Character.isWhitespace(c)) {
395        foundWhitespace = true;
396        break;
397      }
398    }
399    if (foundWhitespace) {
400      throw new MetricsException("Metric name '"+ name +
401          "' contains illegal whitespace character");
402    }
403    // Check if name has already been registered
404    if (metricsMap.containsKey(name)) {
405      throw new MetricsException("Metric name "+ name +" already exists!");
406    }
407  }
408
409  private void checkTagName(String name) {
410    if (tagsMap.containsKey(name)) {
411      throw new MetricsException("Tag "+ name +" already exists!");
412    }
413  }
414
415  /**
416   * Sample all the mutable metrics and put the snapshot in the builder
417   * @param builder to contain the metrics snapshot
418   * @param all get all the metrics even if the values are not changed.
419   */
420  public synchronized void snapshot(MetricsRecordBuilder builder, boolean all) {
421    for (MetricsTag tag : tags()) {
422      builder.add(tag);
423    }
424    for (MutableMetric metric : metrics()) {
425      metric.snapshot(builder, all);
426    }
427  }
428
429  @Override public String toString() {
430    return Objects.toStringHelper(this)
431        .add("info", metricsInfo).add("tags", tags()).add("metrics", metrics())
432        .toString();
433  }
434}