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.hbase.coprocessor.example;
20  
21  import java.io.IOException;
22  
23  import org.apache.commons.logging.Log;
24  import org.apache.commons.logging.LogFactory;
25  import org.apache.hadoop.hbase.CoprocessorEnvironment;
26  import org.apache.hadoop.hbase.HRegionInfo;
27  import org.apache.hadoop.hbase.HTableDescriptor;
28  import org.apache.hadoop.hbase.TableName;
29  import org.apache.hadoop.hbase.coprocessor.BaseMasterObserver;
30  import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment;
31  import org.apache.hadoop.hbase.coprocessor.ObserverContext;
32  import org.apache.hadoop.hbase.metrics.Counter;
33  import org.apache.hadoop.hbase.metrics.Gauge;
34  import org.apache.hadoop.hbase.metrics.MetricRegistry;
35  import org.apache.hadoop.hbase.metrics.Timer;
36  
37  /**
38   * An example coprocessor that collects some metrics to demonstrate the usage of exporting custom
39   * metrics from the coprocessor.
40   *
41   * <p>
42   * These metrics will be available through the regular Hadoop metrics2 sinks (ganglia, opentsdb,
43   * etc) as well as JMX output. You can view a snapshot of the metrics by going to the http web UI
44   * of the master page, something like http://mymasterhost:16010/jmx
45   * </p>
46   * @see ExampleRegionObserverWithMetrics
47   */
48  public class ExampleMasterObserverWithMetrics extends BaseMasterObserver {
49  
50    private static final Log LOG = LogFactory.getLog(ExampleMasterObserverWithMetrics.class);
51  
52    /** This is the Timer metric object to keep track of the current count across invocations */
53    private Timer createTableTimer;
54    private long createTableStartTime = Long.MIN_VALUE;
55  
56    /** This is a Counter object to keep track of disableTable operations */
57    private Counter disableTableCounter;
58  
59    /** Returns the total memory of the process. We will use this to define a gauge metric */
60    private long getTotalMemory() {
61      return Runtime.getRuntime().totalMemory();
62    }
63  
64    /** Returns the max memory of the process. We will use this to define a gauge metric */
65    private long getMaxMemory() {
66      return Runtime.getRuntime().maxMemory();
67    }
68  
69    @Override
70    public void preCreateTable(ObserverContext<MasterCoprocessorEnvironment> ctx,
71                               HTableDescriptor desc, HRegionInfo[] regions) throws IOException {
72      super.preCreateTable(ctx, desc, regions);
73      // we rely on the fact that there is only 1 instance of our MasterObserver. We keep track of
74      // when the operation starts before the operation is executing.
75      this.createTableStartTime = System.currentTimeMillis();
76    }
77  
78    @Override
79    public void postCreateTable(ObserverContext<MasterCoprocessorEnvironment> ctx,
80                                HTableDescriptor desc, HRegionInfo[] regions) throws IOException {
81      super.postCreateTable(ctx, desc, regions);
82      if (this.createTableStartTime > 0) {
83        long time = System.currentTimeMillis() - this.createTableStartTime;
84        LOG.info("Create table took: " + time);
85  
86        // Update the timer metric for the create table operation duration.
87        createTableTimer.updateMillis(time);
88      }
89    }
90  
91    @Override
92    public void preDisableTable(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName) throws IOException {
93      super.preDisableTable(ctx, tableName);
94  
95      // Increment the Counter for disable table operations
96      this.disableTableCounter.increment();
97    }
98  
99    @Override
100   public void start(CoprocessorEnvironment env) throws IOException {
101     super.start(env);
102 
103     // start for the MasterObserver will be called only once in the lifetime of the
104     // server. We will construct and register all metrics that we will track across method
105     // invocations.
106 
107     if (env instanceof MasterCoprocessorEnvironment) {
108       // Obtain the MetricRegistry for the Master. Metrics from this registry will be reported
109       // at the master level per-server.
110       MetricRegistry registry =
111           ((MasterCoprocessorEnvironment) env).getMetricRegistryForMaster();
112 
113       if (createTableTimer == null) {
114         // Create a new Counter, or get the already registered counter.
115         // It is much better to only call this once and save the Counter as a class field instead
116         // of creating the counter every time a coprocessor method is invoked. This will negate
117         // any performance bottleneck coming from map lookups tracking metrics in the registry.
118         createTableTimer = registry.timer("CreateTable");
119 
120         // on stop(), we can remove these registered metrics via calling registry.remove(). But
121         // it is not needed for coprocessors at the master level. If coprocessor is stopped,
122         // the server is stopping anyway, so there will not be any resource leaks.
123       }
124 
125       if (disableTableCounter == null) {
126         disableTableCounter = registry.counter("DisableTable");
127       }
128 
129       // Register a custom gauge. The Gauge object will be registered in the metrics registry and
130       // periodically the getValue() is invoked to obtain the snapshot.
131       registry.register("totalMemory", new Gauge<Long>() {
132         @Override
133         public Long getValue() {
134           return getTotalMemory();
135         }
136       });
137 
138       // Register a custom gauge (Supplier converted into Gauge)
139       registry.register("maxMemory", new Gauge<Long>() {
140         @Override
141         public Long getValue() {
142           return getMaxMemory();
143         }
144 
145       });
146     }
147   }
148 }