View Javadoc

1   /**
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *     http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing, software
13   * distributed under the License is distributed on an "AS IS" BASIS,
14   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15   * See the License for the specific language governing permissions and
16   * limitations under the License.
17   */
18  
19  package org.apache.hadoop.metrics2.lib;
20  
21  import java.util.Collection;
22  import java.util.concurrent.ConcurrentMap;
23  
24  import org.apache.hadoop.hbase.classification.InterfaceAudience;
25  import org.apache.hadoop.metrics2.MetricsException;
26  import org.apache.hadoop.metrics2.MetricsInfo;
27  import org.apache.hadoop.metrics2.MetricsRecordBuilder;
28  import org.apache.hadoop.metrics2.MetricsTag;
29  import org.apache.hadoop.metrics2.impl.MsInfo;
30  
31  import com.google.common.base.Objects;
32  import com.google.common.collect.Maps;
33  
34  /**
35   * An optional metrics registry class for creating and maintaining a
36   * collection of MetricsMutables, making writing metrics source easier.
37   * NOTE: this is a copy of org.apache.hadoop.metrics2.lib.MetricsRegistry with added one
38   *       feature: metrics can be removed. When HADOOP-8313 is fixed, usages of this class
39   *       should be substituted with org.apache.hadoop.metrics2.lib.MetricsRegistry.
40   *       This implementation also provides handy methods for creating metrics
41   *       dynamically.
42   *       Another difference is that metricsMap implementation is substituted with
43   *       thread-safe map, as we allow dynamic metrics additions/removals.
44   */
45  @InterfaceAudience.Private
46  public class DynamicMetricsRegistry {
47    private final ConcurrentMap<String, MutableMetric> metricsMap =
48            Maps.newConcurrentMap();
49    private final ConcurrentMap<String, MetricsTag> tagsMap =
50            Maps.newConcurrentMap();
51    private final MetricsInfo metricsInfo;
52  
53    /**
54     * Construct the registry with a record name
55     * @param name  of the record of the metrics
56     */
57    public DynamicMetricsRegistry(String name) {
58      metricsInfo = Interns.info(name, name);
59    }
60  
61    /**
62     * Construct the registry with a metadata object
63     * @param info  the info object for the metrics record/group
64     */
65    public DynamicMetricsRegistry(MetricsInfo info) {
66      metricsInfo = info;
67    }
68  
69    /**
70     * @return the info object of the metrics registry
71     */
72    public MetricsInfo info() {
73      return metricsInfo;
74    }
75  
76    /**
77     * Get a metric by name
78     * @param name  of the metric
79     * @return the metric object
80     */
81    public MutableMetric get(String name) {
82      return metricsMap.get(name);
83    }
84  
85    /**
86     * Get a tag by name
87     * @param name  of the tag
88     * @return the tag object
89     */
90    public MetricsTag getTag(String name) {
91      return tagsMap.get(name);
92    }
93  
94    /**
95     * Create a mutable integer counter
96     * @param name  of the metric
97     * @param desc  metric description
98     * @param iVal  initial value
99     * @return a new counter object
100    */
101   public MutableCounterInt newCounter(String name, String desc, int iVal) {
102     return newCounter(new MetricsInfoImpl(name, desc), iVal);
103   }
104 
105   /**
106    * Create a mutable integer counter
107    * @param info  metadata of the metric
108    * @param iVal  initial value
109    * @return a new counter object
110    */
111   public MutableCounterInt newCounter(MetricsInfo info, int iVal) {
112     MutableCounterInt ret = new MutableCounterInt(info, iVal);
113     return addNewMetricIfAbsent(info.name(), ret, MutableCounterInt.class);
114   }
115 
116   /**
117    * Create a mutable long integer counter
118    * @param name  of the metric
119    * @param desc  metric description
120    * @param iVal  initial value
121    * @return a new counter object
122    */
123   public MutableCounterLong newCounter(String name, String desc, long iVal) {
124     return newCounter(new MetricsInfoImpl(name, desc), iVal);
125   }
126 
127   /**
128    * Create a mutable long integer counter
129    * @param info  metadata of the metric
130    * @param iVal  initial value
131    * @return a new counter object
132    */
133   public MutableCounterLong newCounter(MetricsInfo info, long iVal) {
134     MutableCounterLong ret = new MutableCounterLong(info, iVal);
135     return addNewMetricIfAbsent(info.name(), ret, MutableCounterLong.class);
136   }
137 
138   /**
139    * Create a mutable integer gauge
140    * @param name  of the metric
141    * @param desc  metric description
142    * @param iVal  initial value
143    * @return a new gauge object
144    */
145   public MutableGaugeInt newGauge(String name, String desc, int iVal) {
146     return newGauge(new MetricsInfoImpl(name, desc), iVal);
147   }
148   /**
149    * Create a mutable integer gauge
150    * @param info  metadata of the metric
151    * @param iVal  initial value
152    * @return a new gauge object
153    */
154   public MutableGaugeInt newGauge(MetricsInfo info, int iVal) {
155     MutableGaugeInt ret = new MutableGaugeInt(info, iVal);
156     return addNewMetricIfAbsent(info.name(), ret, MutableGaugeInt.class);
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(new MetricsInfoImpl(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 MutableGaugeLong newGauge(MetricsInfo info, long iVal) {
177     MutableGaugeLong ret = new MutableGaugeLong(info, iVal);
178     return addNewMetricIfAbsent(info.name(), ret, MutableGaugeLong.class);
179   }
180 
181   /**
182    * Create a mutable metric with stats
183    * @param name  of the metric
184    * @param desc  metric description
185    * @param sampleName  of the metric (e.g., "Ops")
186    * @param valueName   of the metric (e.g., "Time" or "Latency")
187    * @param extended    produce extended stat (stdev, min/max etc.) if true.
188    * @return a new mutable stat metric object
189    */
190   public MutableStat newStat(String name, String desc,
191       String sampleName, String valueName, boolean extended) {
192     MutableStat ret =
193         new MutableStat(name, desc, sampleName, valueName, extended);
194     return addNewMetricIfAbsent(name, ret, MutableStat.class);
195   }
196 
197   /**
198    * Create a mutable metric with stats
199    * @param name  of the metric
200    * @param desc  metric description
201    * @param sampleName  of the metric (e.g., "Ops")
202    * @param valueName   of the metric (e.g., "Time" or "Latency")
203    * @return a new mutable metric object
204    */
205   public MutableStat newStat(String name, String desc,
206                              String sampleName, String valueName) {
207     return newStat(name, desc, sampleName, valueName, false);
208   }
209 
210   /**
211    * Create a mutable rate metric
212    * @param name  of the metric
213    * @return a new mutable metric object
214    */
215   public MutableRate newRate(String name) {
216     return newRate(name, name, false);
217   }
218 
219   /**
220    * Create a mutable rate metric
221    * @param name  of the metric
222    * @param description of the metric
223    * @return a new mutable rate metric object
224    */
225   public MutableRate newRate(String name, String description) {
226     return newRate(name, description, false);
227   }
228 
229   /**
230    * Create a mutable rate metric (for throughput measurement)
231    * @param name  of the metric
232    * @param desc  description
233    * @param extended  produce extended stat (stdev/min/max etc.) if true
234    * @return a new mutable rate metric object
235    */
236   public MutableRate newRate(String name, String desc, boolean extended) {
237     return newRate(name, desc, extended, true);
238   }
239 
240   @InterfaceAudience.Private
241   public MutableRate newRate(String name, String desc,
242       boolean extended, boolean returnExisting) {
243     if (returnExisting) {
244       MutableMetric rate = metricsMap.get(name);
245       if (rate != null) {
246         if (rate instanceof MutableRate) return (MutableRate) rate;
247         throw new MetricsException("Unexpected metrics type "+ rate.getClass()
248                                    +" for "+ name);
249       }
250     }
251     MutableRate ret = new MutableRate(name, desc, extended);
252     return addNewMetricIfAbsent(name, ret, MutableRate.class);
253   }
254 
255   /**
256    * Create a new histogram.
257    * @param name Name of the histogram.
258    * @return A new MutableHistogram
259    */
260   public MutableHistogram newHistogram(String name) {
261      return newHistogram(name, "");
262   }
263 
264   /**
265    * Create a new histogram.
266    * @param name The name of the histogram
267    * @param desc The description of the data in the histogram.
268    * @return A new MutableHistogram
269    */
270   public MutableHistogram newHistogram(String name, String desc) {
271     MutableHistogram histo = new MutableHistogram(name, desc);
272     return addNewMetricIfAbsent(name, histo, MutableHistogram.class);
273   }
274   
275   /**
276    * Create a new histogram with time range counts.
277    * @param name Name of the histogram.
278    * @return A new MutableTimeHistogram
279    */
280   public MutableTimeHistogram newTimeHistogram(String name) {
281      return newTimeHistogram(name, "");
282   }
283 
284   /**
285    * Create a new histogram with time range counts.
286    * @param name The name of the histogram
287    * @param desc The description of the data in the histogram.
288    * @return A new MutableTimeHistogram
289    */
290   public MutableTimeHistogram newTimeHistogram(String name, String desc) {
291     MutableTimeHistogram histo = new MutableTimeHistogram(name, desc);
292     return addNewMetricIfAbsent(name, histo, MutableTimeHistogram.class);
293   }
294   
295   /**
296    * Create a new histogram with size range counts.
297    * @param name Name of the histogram.
298    * @return A new MutableSizeHistogram
299    */
300   public MutableSizeHistogram newSizeHistogram(String name) {
301      return newSizeHistogram(name, "");
302   }
303 
304   /**
305    * Create a new histogram with size range counts.
306    * @param name The name of the histogram
307    * @param desc The description of the data in the histogram.
308    * @return A new MutableSizeHistogram
309    */
310   public MutableSizeHistogram newSizeHistogram(String name, String desc) {
311     MutableSizeHistogram histo = new MutableSizeHistogram(name, desc);
312     return addNewMetricIfAbsent(name, histo, MutableSizeHistogram.class);
313   }
314 
315   /**
316    * Create a new MutableQuantile(A more accurate histogram).
317    * @param name The name of the histogram
318    * @return a new MutableQuantile
319    */
320   public MetricMutableQuantiles newQuantile(String name) {
321     return newQuantile(name, "");
322   }
323 
324   public MetricMutableQuantiles newQuantile(String name, String desc) {
325     MetricMutableQuantiles histo = new MetricMutableQuantiles(name, desc, "Ops", "", 60);
326     return addNewMetricIfAbsent(name, histo, MetricMutableQuantiles.class);
327   }
328 
329   synchronized void add(String name, MutableMetric metric) {
330     addNewMetricIfAbsent(name, metric, MutableMetric.class);
331   }
332 
333   /**
334    * Add sample to a stat metric by name.
335    * @param name  of the metric
336    * @param value of the snapshot to add
337    */
338   public void add(String name, long value) {
339     MutableMetric m = metricsMap.get(name);
340 
341     if (m != null) {
342       if (m instanceof MutableStat) {
343         ((MutableStat) m).add(value);
344       }
345       else {
346         throw new MetricsException("Unsupported add(value) for metric "+ name);
347       }
348     }
349     else {
350       metricsMap.put(name, newRate(name)); // default is a rate metric
351       add(name, value);
352     }
353   }
354 
355   /**
356    * Set the metrics context tag
357    * @param name of the context
358    * @return the registry itself as a convenience
359    */
360   public DynamicMetricsRegistry setContext(String name) {
361     return tag(MsInfo.Context, name, true);
362   }
363 
364   /**
365    * Add a tag to the metrics
366    * @param name  of the tag
367    * @param description of the tag
368    * @param value of the tag
369    * @return the registry (for keep adding tags)
370    */
371   public DynamicMetricsRegistry tag(String name, String description, String value) {
372     return tag(name, description, value, false);
373   }
374 
375   /**
376    * Add a tag to the metrics
377    * @param name  of the tag
378    * @param description of the tag
379    * @param value of the tag
380    * @param override  existing tag if true
381    * @return the registry (for keep adding tags)
382    */
383   public DynamicMetricsRegistry tag(String name, String description, String value,
384                              boolean override) {
385     return tag(new MetricsInfoImpl(name, description), value, override);
386   }
387 
388   /**
389    * Add a tag to the metrics
390    * @param info  metadata of the tag
391    * @param value of the tag
392    * @param override existing tag if true
393    * @return the registry (for keep adding tags etc.)
394    */
395   public DynamicMetricsRegistry tag(MetricsInfo info, String value, boolean override) {
396     MetricsTag tag = Interns.tag(info, value);
397 
398     if (!override) {
399       MetricsTag existing = tagsMap.putIfAbsent(info.name(), tag);
400       if (existing != null) {
401         throw new MetricsException("Tag "+ info.name() +" already exists!");
402       }
403       return this;
404     }
405 
406     tagsMap.put(info.name(), tag);
407 
408     return this;
409   }
410 
411   public DynamicMetricsRegistry tag(MetricsInfo info, String value) {
412     return tag(info, value, false);
413   }
414 
415   Collection<MetricsTag> tags() {
416     return tagsMap.values();
417   }
418 
419   Collection<MutableMetric> metrics() {
420     return metricsMap.values();
421   }
422 
423   /**
424    * Sample all the mutable metrics and put the snapshot in the builder
425    * @param builder to contain the metrics snapshot
426    * @param all get all the metrics even if the values are not changed.
427    */
428   public void snapshot(MetricsRecordBuilder builder, boolean all) {
429     for (MetricsTag tag : tags()) {
430       builder.add(tag);
431     }
432     for (MutableMetric metric : metrics()) {
433       metric.snapshot(builder, all);
434     }
435   }
436 
437   @Override public String toString() {
438     return Objects.toStringHelper(this)
439         .add("info", metricsInfo).add("tags", tags()).add("metrics", metrics())
440         .toString();
441   }
442 
443   /**
444    * Removes metric by name
445    * @param name name of the metric to remove
446    */
447   public void removeMetric(String name) {
448     metricsMap.remove(name);
449   }
450 
451   /**
452    * Get a MetricMutableGaugeLong from the storage.  If it is not there atomically put it.
453    *
454    * @param gaugeName              name of the gauge to create or get.
455    * @param potentialStartingValue value of the new gauge if we have to create it.
456    */
457   public MutableGaugeLong getLongGauge(String gaugeName, long potentialStartingValue) {
458     //Try and get the guage.
459     MutableMetric metric = metricsMap.get(gaugeName);
460 
461     //If it's not there then try and put a new one in the storage.
462     if (metric == null) {
463 
464       //Create the potential new gauge.
465       MutableGaugeLong newGauge = new MutableGaugeLong(new MetricsInfoImpl(gaugeName, ""),
466               potentialStartingValue);
467 
468       // Try and put the gauge in.  This is atomic.
469       metric = metricsMap.putIfAbsent(gaugeName, newGauge);
470 
471       //If the value we get back is null then the put was successful and we will return that.
472       //otherwise gaugeLong should contain the thing that was in before the put could be completed.
473       if (metric == null) {
474         return newGauge;
475       }
476     }
477 
478     if (!(metric instanceof MutableGaugeLong)) {
479       throw new MetricsException("Metric already exists in registry for metric name: " + gaugeName +
480               " and not of type MetricMutableGaugeLong");
481     }
482 
483     return (MutableGaugeLong) metric;
484   }
485 
486   /**
487    * Get a MetricMutableCounterLong from the storage.  If it is not there atomically put it.
488    *
489    * @param counterName            Name of the counter to get
490    * @param potentialStartingValue starting value if we have to create a new counter
491    */
492   public MutableCounterLong getLongCounter(String counterName, long potentialStartingValue) {
493     //See getLongGauge for description on how this works.
494     MutableMetric counter = metricsMap.get(counterName);
495     if (counter == null) {
496       MutableCounterLong newCounter =
497               new MutableCounterLong(new MetricsInfoImpl(counterName, ""), potentialStartingValue);
498       counter = metricsMap.putIfAbsent(counterName, newCounter);
499       if (counter == null) {
500         return newCounter;
501       }
502     }
503 
504 
505     if (!(counter instanceof MutableCounterLong)) {
506       throw new MetricsException("Metric already exists in registry for metric name: " +
507               counterName + " and not of type MetricMutableCounterLong");
508     }
509 
510     return (MutableCounterLong) counter;
511   }
512 
513   public MutableHistogram getHistogram(String histoName) {
514     //See getLongGauge for description on how this works.
515     MutableMetric histo = metricsMap.get(histoName);
516     if (histo == null) {
517       MutableHistogram newCounter =
518           new MutableHistogram(new MetricsInfoImpl(histoName, ""));
519       histo = metricsMap.putIfAbsent(histoName, newCounter);
520       if (histo == null) {
521         return newCounter;
522       }
523     }
524 
525 
526     if (!(histo instanceof MutableHistogram)) {
527       throw new MetricsException("Metric already exists in registry for metric name: " +
528           histoName + " and not of type MutableHistogram");
529     }
530 
531     return (MutableHistogram) histo;
532   }
533 
534   public MetricMutableQuantiles getQuantile(String histoName) {
535     //See getLongGauge for description on how this works.
536     MutableMetric histo = metricsMap.get(histoName);
537     if (histo == null) {
538       MetricMutableQuantiles newCounter =
539           new MetricMutableQuantiles(histoName, "", "Ops", "", 60);
540       histo = metricsMap.putIfAbsent(histoName, newCounter);
541       if (histo == null) {
542         return newCounter;
543       }
544     }
545 
546 
547     if (!(histo instanceof MetricMutableQuantiles)) {
548       throw new MetricsException("Metric already exists in registry for metric name: " +
549           histoName + " and not of type MutableHistogram");
550     }
551 
552     return (MetricMutableQuantiles) histo;
553   }
554 
555   private<T extends MutableMetric> T
556   addNewMetricIfAbsent(String name,
557                        T ret,
558                        Class<T> metricClass) {
559     //If the value we get back is null then the put was successful and we will
560     // return that. Otherwise metric should contain the thing that was in
561     // before the put could be completed.
562     MutableMetric metric = metricsMap.putIfAbsent(name, ret);
563     if (metric == null) {
564       return ret;
565     }
566 
567     return returnExistingWithCast(metric, metricClass, name);
568   }
569 
570   @SuppressWarnings("unchecked")
571   private<T> T returnExistingWithCast(MutableMetric metric,
572                                       Class<T> metricClass, String name) {
573     if (!metricClass.isAssignableFrom(metric.getClass())) {
574       throw new MetricsException("Metric already exists in registry for metric name: " +
575               name + " and not of type " + metricClass +
576               " but instead of type " + metric.getClass());
577     }
578 
579     return (T) metric;
580   }
581 
582   public void clearMetrics() {
583     metricsMap.clear();
584   }
585 }