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 }