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    
019    package org.apache.hadoop.metrics2.lib;
020    
021    import java.util.Collection;
022    import java.util.Map;
023    
024    import com.google.common.collect.Maps;
025    import com.google.common.base.Objects;
026    
027    import org.apache.hadoop.classification.InterfaceAudience;
028    import org.apache.hadoop.classification.InterfaceStability;
029    import org.apache.hadoop.metrics2.MetricsInfo;
030    import org.apache.hadoop.metrics2.MetricsException;
031    import org.apache.hadoop.metrics2.MetricsRecordBuilder;
032    import org.apache.hadoop.metrics2.MetricsTag;
033    import 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
041    public 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 metric with stats
185       * @param name  of the metric
186       * @param desc  metric description
187       * @param sampleName  of the metric (e.g., "Ops")
188       * @param valueName   of the metric (e.g., "Time" or "Latency")
189       * @param extended    produce extended stat (stdev, min/max etc.) if true.
190       * @return a new mutable stat metric object
191       */
192      public synchronized MutableStat newStat(String name, String desc,
193          String sampleName, String valueName, boolean extended) {
194        checkMetricName(name);
195        MutableStat ret =
196            new MutableStat(name, desc, sampleName, valueName, extended);
197        metricsMap.put(name, ret);
198        return ret;
199      }
200    
201      /**
202       * Create a mutable metric with stats
203       * @param name  of the metric
204       * @param desc  metric description
205       * @param sampleName  of the metric (e.g., "Ops")
206       * @param valueName   of the metric (e.g., "Time" or "Latency")
207       * @return a new mutable metric object
208       */
209      public MutableStat newStat(String name, String desc,
210                                 String sampleName, String valueName) {
211        return newStat(name, desc, sampleName, valueName, false);
212      }
213    
214      /**
215       * Create a mutable rate metric
216       * @param name  of the metric
217       * @return a new mutable metric object
218       */
219      public MutableRate newRate(String name) {
220        return newRate(name, name, false);
221      }
222    
223      /**
224       * Create a mutable rate metric
225       * @param name  of the metric
226       * @param description of the metric
227       * @return a new mutable rate metric object
228       */
229      public MutableRate newRate(String name, String description) {
230        return newRate(name, description, false);
231      }
232    
233      /**
234       * Create a mutable rate metric (for throughput measurement)
235       * @param name  of the metric
236       * @param desc  description
237       * @param extended  produce extended stat (stdev/min/max etc.) if true
238       * @return a new mutable rate metric object
239       */
240      public MutableRate newRate(String name, String desc, boolean extended) {
241        return newRate(name, desc, extended, true);
242      }
243    
244      @InterfaceAudience.Private
245      public synchronized MutableRate newRate(String name, String desc,
246          boolean extended, boolean returnExisting) {
247        if (returnExisting) {
248          MutableMetric rate = metricsMap.get(name);
249          if (rate != null) {
250            if (rate instanceof MutableRate) return (MutableRate) rate;
251            throw new MetricsException("Unexpected metrics type "+ rate.getClass()
252                                       +" for "+ name);
253          }
254        }
255        checkMetricName(name);
256        MutableRate ret = new MutableRate(name, desc, extended);
257        metricsMap.put(name, ret);
258        return ret;
259      }
260    
261      synchronized void add(String name, MutableMetric metric) {
262        checkMetricName(name);
263        metricsMap.put(name, metric);
264      }
265    
266      /**
267       * Add sample to a stat metric by name.
268       * @param name  of the metric
269       * @param value of the snapshot to add
270       */
271      public synchronized void add(String name, long value) {
272        MutableMetric m = metricsMap.get(name);
273    
274        if (m != null) {
275          if (m instanceof MutableStat) {
276            ((MutableStat) m).add(value);
277          }
278          else {
279            throw new MetricsException("Unsupported add(value) for metric "+ name);
280          }
281        }
282        else {
283          metricsMap.put(name, newRate(name)); // default is a rate metric
284          add(name, value);
285        }
286      }
287    
288      /**
289       * Set the metrics context tag
290       * @param name of the context
291       * @return the registry itself as a convenience
292       */
293      public MetricsRegistry setContext(String name) {
294        return tag(MsInfo.Context, name, true);
295      }
296    
297      /**
298       * Add a tag to the metrics
299       * @param name  of the tag
300       * @param description of the tag
301       * @param value of the tag
302       * @return the registry (for keep adding tags)
303       */
304      public MetricsRegistry tag(String name, String description, String value) {
305        return tag(name, description, value, false);
306      }
307    
308      /**
309       * Add a tag to the metrics
310       * @param name  of the tag
311       * @param description of the tag
312       * @param value of the tag
313       * @param override  existing tag if true
314       * @return the registry (for keep adding tags)
315       */
316      public MetricsRegistry tag(String name, String description, String value,
317                                 boolean override) {
318        return tag(Interns.info(name, description), value, override);
319      }
320    
321      /**
322       * Add a tag to the metrics
323       * @param info  metadata of the tag
324       * @param value of the tag
325       * @param override existing tag if true
326       * @return the registry (for keep adding tags etc.)
327       */
328      public synchronized
329      MetricsRegistry tag(MetricsInfo info, String value, boolean override) {
330        if (!override) checkTagName(info.name());
331        tagsMap.put(info.name(), Interns.tag(info, value));
332        return this;
333      }
334    
335      public MetricsRegistry tag(MetricsInfo info, String value) {
336        return tag(info, value, false);
337      }
338    
339      Collection<MetricsTag> tags() {
340        return tagsMap.values();
341      }
342    
343      Collection<MutableMetric> metrics() {
344        return metricsMap.values();
345      }
346    
347      private void checkMetricName(String name) {
348        if (metricsMap.containsKey(name)) {
349          throw new MetricsException("Metric name "+ name +" already exists!");
350        }
351      }
352    
353      private void checkTagName(String name) {
354        if (tagsMap.containsKey(name)) {
355          throw new MetricsException("Tag "+ name +" already exists!");
356        }
357      }
358    
359      /**
360       * Sample all the mutable metrics and put the snapshot in the builder
361       * @param builder to contain the metrics snapshot
362       * @param all get all the metrics even if the values are not changed.
363       */
364      public synchronized void snapshot(MetricsRecordBuilder builder, boolean all) {
365        for (MetricsTag tag : tags()) {
366          builder.add(tag);
367        }
368        for (MutableMetric metric : metrics()) {
369          metric.snapshot(builder, all);
370        }
371      }
372    
373      @Override public String toString() {
374        return Objects.toStringHelper(this)
375            .add("info", metricsInfo).add("tags", tags()).add("metrics", metrics())
376            .toString();
377      }
378    }