View Javadoc

1   /**
2    * Copyright 2010 The Apache Software Foundation
3    *
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *     http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing, software
15   * distributed under the License is distributed on an "AS IS" BASIS,
16   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17   * See the License for the specific language governing permissions and
18   * limitations under the License.
19   */
20  package org.apache.hadoop.hbase.metrics;
21  
22  import java.util.ArrayList;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.concurrent.ConcurrentHashMap;
26  
27  import javax.management.AttributeNotFoundException;
28  import javax.management.MBeanAttributeInfo;
29  import javax.management.MBeanException;
30  import javax.management.MBeanInfo;
31  import javax.management.ReflectionException;
32  import org.apache.hadoop.hbase.metrics.histogram.MetricsHistogram;
33  
34  import com.yammer.metrics.stats.Snapshot;
35  import org.apache.commons.logging.Log;
36  import org.apache.commons.logging.LogFactory;
37  import org.apache.hadoop.metrics.util.MetricsBase;
38  import org.apache.hadoop.metrics.util.MetricsDynamicMBeanBase;
39  import org.apache.hadoop.metrics.util.MetricsRegistry;
40  
41  /**
42   * Extends the Hadoop MetricsDynamicMBeanBase class to provide JMX support for
43   * custom HBase MetricsBase implementations.  MetricsDynamicMBeanBase ignores
44   * registered MetricsBase instance that are not instances of one of the
45   * org.apache.hadoop.metrics.util implementations.
46   *
47   */
48  public class MetricsMBeanBase extends MetricsDynamicMBeanBase {
49  
50    private static final Log LOG = LogFactory.getLog("org.apache.hadoop.hbase.metrics");
51  
52    protected final MetricsRegistry registry;
53    protected final String description;
54    protected int registryLength;
55    /** HBase MetricsBase implementations that MetricsDynamicMBeanBase does
56     * not understand
57     */
58    protected Map<String, MetricsBase> extendedAttributes =
59        new ConcurrentHashMap<String, MetricsBase>();
60    protected MBeanInfo extendedInfo;
61  
62    protected MetricsMBeanBase( MetricsRegistry mr, String description ) {
63      super(copyMinusHBaseMetrics(mr), description);
64      this.registry = mr;
65      this.description = description;
66      this.init();
67    }
68  
69    /*
70     * @param mr MetricsRegistry.
71     * @return A copy of the passed MetricsRegistry minus the hbase metrics
72     */
73    private static MetricsRegistry copyMinusHBaseMetrics(final MetricsRegistry mr) {
74      MetricsRegistry copy = new MetricsRegistry();
75      for (MetricsBase metric : mr.getMetricsList()) {
76        if (metric instanceof MetricsRate || metric instanceof MetricsString ||
77            metric instanceof MetricsHistogram || metric instanceof ExactCounterMetric) {
78          continue;
79        }
80        copy.add(metric.getName(), metric);
81      }
82      return copy;
83    }
84  
85    protected void init() {
86      List<MBeanAttributeInfo> attributes = new ArrayList<MBeanAttributeInfo>();
87      MBeanInfo parentInfo = super.getMBeanInfo();
88      List<String> parentAttributes = new ArrayList<String>();
89      for (MBeanAttributeInfo attr : parentInfo.getAttributes()) {
90        attributes.add(attr);
91        parentAttributes.add(attr.getName());
92      }
93  
94      this.registryLength = this.registry.getMetricsList().size();
95  
96      for (MetricsBase metric : this.registry.getMetricsList()) {
97        if (metric.getName() == null || parentAttributes.contains(metric.getName()))
98          continue;
99  
100       // add on custom HBase metric types
101       if (metric instanceof MetricsRate) {
102         attributes.add( new MBeanAttributeInfo(metric.getName(),
103             "java.lang.Float", metric.getDescription(), true, false, false) );
104         extendedAttributes.put(metric.getName(), metric);
105       } else if (metric instanceof MetricsString) {
106         attributes.add( new MBeanAttributeInfo(metric.getName(),
107             "java.lang.String", metric.getDescription(), true, false, false) );
108         extendedAttributes.put(metric.getName(), metric);
109         LOG.info("MetricsString added: " + metric.getName());
110       } else if (metric instanceof MetricsHistogram) {
111 
112         String metricName = metric.getName() + MetricsHistogram.NUM_OPS_METRIC_NAME;
113         attributes.add(new MBeanAttributeInfo(metricName,
114             "java.lang.Long", metric.getDescription(), true, false, false));
115         extendedAttributes.put(metricName, metric);
116 
117         metricName = metric.getName() + MetricsHistogram.MIN_METRIC_NAME;
118         attributes.add(new MBeanAttributeInfo(metricName,
119             "java.lang.Long", metric.getDescription(), true, false, false));
120         extendedAttributes.put(metricName, metric);
121 
122         metricName = metric.getName() + MetricsHistogram.MAX_METRIC_NAME;
123         attributes.add(new MBeanAttributeInfo(metricName,
124             "java.lang.Long", metric.getDescription(), true, false, false));
125         extendedAttributes.put(metricName, metric);
126 
127         metricName = metric.getName() + MetricsHistogram.MEAN_METRIC_NAME;
128         attributes.add(new MBeanAttributeInfo(metricName,
129             "java.lang.Float", metric.getDescription(), true, false, false));
130         extendedAttributes.put(metricName, metric);
131 
132         metricName = metric.getName() + MetricsHistogram.STD_DEV_METRIC_NAME;
133         attributes.add(new MBeanAttributeInfo(metricName,
134             "java.lang.Float", metric.getDescription(), true, false, false));
135         extendedAttributes.put(metricName, metric);
136 
137         metricName = metric.getName() + MetricsHistogram.MEDIAN_METRIC_NAME;
138         attributes.add(new MBeanAttributeInfo(metricName,
139             "java.lang.Float", metric.getDescription(), true, false, false));
140         extendedAttributes.put(metricName, metric);
141 
142         metricName = metric.getName() + MetricsHistogram.SEVENTY_FIFTH_PERCENTILE_METRIC_NAME;
143         attributes.add(new MBeanAttributeInfo(metricName,
144             "java.lang.Float", metric.getDescription(), true, false, false));
145         extendedAttributes.put(metricName, metric);
146 
147         metricName = metric.getName() + MetricsHistogram.NINETY_FIFTH_PERCENTILE_METRIC_NAME;
148         attributes.add(new MBeanAttributeInfo(metricName,
149             "java.lang.Float", metric.getDescription(), true, false, false));
150         extendedAttributes.put(metricName, metric);
151 
152         metricName = metric.getName() + MetricsHistogram.NINETY_NINETH_PERCENTILE_METRIC_NAME;
153         attributes.add(new MBeanAttributeInfo(metricName,
154             "java.lang.Float", metric.getDescription(), true, false, false));
155         extendedAttributes.put(metricName, metric);
156       }
157       // else, its probably a hadoop metric already registered. Skip it.
158     }
159 
160     LOG.info("new MBeanInfo");
161     this.extendedInfo = new MBeanInfo( this.getClass().getName(),
162         this.description, attributes.toArray( new MBeanAttributeInfo[0] ),
163         parentInfo.getConstructors(), parentInfo.getOperations(),
164         parentInfo.getNotifications() );
165   }
166 
167   private void checkAndUpdateAttributes() {
168     if (this.registryLength != this.registry.getMetricsList().size())
169       this.init();
170   }
171 
172   @Override
173   public Object getAttribute( String name )
174       throws AttributeNotFoundException, MBeanException,
175       ReflectionException {
176 
177     if (name == null) {
178       throw new IllegalArgumentException("Attribute name is NULL");
179     }
180 
181     /*
182      * Ugly.  Since MetricsDynamicMBeanBase implementation is private,
183      * we need to first check the parent class for the attribute.
184      * In case that the MetricsRegistry contents have changed, this will
185      * allow the parent to update it's internal structures (which we rely on
186      * to update our own.
187      */
188     try {
189       return super.getAttribute(name);
190     } catch (AttributeNotFoundException ex) {
191 
192       checkAndUpdateAttributes();
193 
194       MetricsBase metric = this.extendedAttributes.get(name);
195       if (metric != null) {
196         if (metric instanceof MetricsRate) {
197           return ((MetricsRate) metric).getPreviousIntervalValue();
198         } else if (metric instanceof MetricsString) {
199           return ((MetricsString)metric).getValue();
200         } else if (metric instanceof MetricsHistogram)  {
201           MetricsHistogram hist = (MetricsHistogram) metric;
202           if (name.endsWith(MetricsHistogram.NUM_OPS_METRIC_NAME)) {
203             return hist.getCount();
204           } else if (name.endsWith(MetricsHistogram.MIN_METRIC_NAME)) {
205             return hist.getMin();
206           } else if (name.endsWith(MetricsHistogram.MAX_METRIC_NAME)) {
207             return hist.getMax();
208           } else if (name.endsWith(MetricsHistogram.MEAN_METRIC_NAME)) {
209             return (float) hist.getMean();
210           } else if (name.endsWith(MetricsHistogram.STD_DEV_METRIC_NAME)) {
211             return (float) hist.getStdDev();
212           } else if (name.endsWith(MetricsHistogram.MEDIAN_METRIC_NAME)) {
213             Snapshot s = hist.getSnapshot();
214             return (float) s.getMedian();
215           } else if (name.endsWith(MetricsHistogram.SEVENTY_FIFTH_PERCENTILE_METRIC_NAME)) {
216             Snapshot s = hist.getSnapshot();
217             return (float) s.get75thPercentile();
218           } else if (name.endsWith(MetricsHistogram.NINETY_FIFTH_PERCENTILE_METRIC_NAME)) {
219             Snapshot s = hist.getSnapshot();
220             return (float) s.get95thPercentile();
221           } else if (name.endsWith(MetricsHistogram.NINETY_NINETH_PERCENTILE_METRIC_NAME)) {
222             Snapshot s = hist.getSnapshot();
223             return (float) s.get99thPercentile();
224           }
225 
226         } else {
227           LOG.warn( String.format("unknown metrics type %s for attribute %s",
228                         metric.getClass().getName(), name) );
229         }
230       }
231     }
232 
233     throw new AttributeNotFoundException();
234   }
235 
236   @Override
237   public MBeanInfo getMBeanInfo() {
238     return this.extendedInfo;
239   }
240 
241 }