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