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 019package org.apache.hadoop.hbase.coprocessor.example; 020 021import java.io.IOException; 022import java.util.Optional; 023import org.apache.hadoop.hbase.CoprocessorEnvironment; 024import org.apache.hadoop.hbase.TableName; 025import org.apache.hadoop.hbase.client.RegionInfo; 026import org.apache.hadoop.hbase.client.TableDescriptor; 027import org.apache.hadoop.hbase.coprocessor.MasterCoprocessor; 028import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment; 029import org.apache.hadoop.hbase.coprocessor.MasterObserver; 030import org.apache.hadoop.hbase.coprocessor.ObserverContext; 031import org.apache.hadoop.hbase.metrics.Counter; 032import org.apache.hadoop.hbase.metrics.Gauge; 033import org.apache.hadoop.hbase.metrics.MetricRegistry; 034import org.apache.hadoop.hbase.metrics.Timer; 035import org.apache.yetus.audience.InterfaceAudience; 036import org.slf4j.Logger; 037import org.slf4j.LoggerFactory; 038 039/** 040 * An example coprocessor that collects some metrics to demonstrate the usage of exporting custom 041 * metrics from the coprocessor. 042 * 043 * <p> 044 * These metrics will be available through the regular Hadoop metrics2 sinks (ganglia, opentsdb, 045 * etc) as well as JMX output. You can view a snapshot of the metrics by going to the http web UI 046 * of the master page, something like http://mymasterhost:16010/jmx 047 * </p> 048 * @see ExampleRegionObserverWithMetrics 049 */ 050@InterfaceAudience.Private 051public class ExampleMasterObserverWithMetrics implements MasterCoprocessor, MasterObserver { 052 @Override 053 public Optional<MasterObserver> getMasterObserver() { 054 return Optional.of(this); 055 } 056 057 private static final Logger LOG = LoggerFactory.getLogger(ExampleMasterObserverWithMetrics.class); 058 059 /** This is the Timer metric object to keep track of the current count across invocations */ 060 private Timer createTableTimer; 061 private long createTableStartTime = Long.MIN_VALUE; 062 063 /** This is a Counter object to keep track of disableTable operations */ 064 private Counter disableTableCounter; 065 066 /** Returns the total memory of the process. We will use this to define a gauge metric */ 067 private long getTotalMemory() { 068 return Runtime.getRuntime().totalMemory(); 069 } 070 071 /** Returns the max memory of the process. We will use this to define a gauge metric */ 072 private long getMaxMemory() { 073 return Runtime.getRuntime().maxMemory(); 074 } 075 076 @Override 077 public void preCreateTable(ObserverContext<MasterCoprocessorEnvironment> ctx, 078 TableDescriptor desc, RegionInfo[] regions) throws IOException { 079 // we rely on the fact that there is only 1 instance of our MasterObserver. We keep track of 080 // when the operation starts before the operation is executing. 081 this.createTableStartTime = System.currentTimeMillis(); 082 } 083 084 @Override 085 public void postCreateTable(ObserverContext<MasterCoprocessorEnvironment> ctx, 086 TableDescriptor desc, RegionInfo[] regions) throws IOException { 087 if (this.createTableStartTime > 0) { 088 long time = System.currentTimeMillis() - this.createTableStartTime; 089 LOG.info("Create table took: " + time); 090 091 // Update the timer metric for the create table operation duration. 092 createTableTimer.updateMillis(time); 093 } 094 } 095 096 @Override 097 public void preDisableTable(ObserverContext<MasterCoprocessorEnvironment> ctx, TableName tableName) throws IOException { 098 // Increment the Counter for disable table operations 099 this.disableTableCounter.increment(); 100 } 101 102 @Override 103 public void start(CoprocessorEnvironment env) throws IOException { 104 // start for the MasterObserver will be called only once in the lifetime of the 105 // server. We will construct and register all metrics that we will track across method 106 // invocations. 107 108 if (env instanceof MasterCoprocessorEnvironment) { 109 // Obtain the MetricRegistry for the Master. Metrics from this registry will be reported 110 // at the master level per-server. 111 MetricRegistry registry = 112 ((MasterCoprocessorEnvironment) env).getMetricRegistryForMaster(); 113 114 if (createTableTimer == null) { 115 // Create a new Counter, or get the already registered counter. 116 // It is much better to only call this once and save the Counter as a class field instead 117 // of creating the counter every time a coprocessor method is invoked. This will negate 118 // any performance bottleneck coming from map lookups tracking metrics in the registry. 119 createTableTimer = registry.timer("CreateTable"); 120 121 // on stop(), we can remove these registered metrics via calling registry.remove(). But 122 // it is not needed for coprocessors at the master level. If coprocessor is stopped, 123 // the server is stopping anyway, so there will not be any resource leaks. 124 } 125 126 if (disableTableCounter == null) { 127 disableTableCounter = registry.counter("DisableTable"); 128 } 129 130 // Register a custom gauge. The Gauge object will be registered in the metrics registry and 131 // periodically the getValue() is invoked to obtain the snapshot. 132 registry.register("totalMemory", new Gauge<Long>() { 133 @Override 134 public Long getValue() { 135 return getTotalMemory(); 136 } 137 }); 138 139 // Register a custom gauge using Java-8 lambdas (Supplier converted into Gauge) 140 registry.register("maxMemory", this::getMaxMemory); 141 } 142 } 143}