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 */ 018package org.apache.hadoop.hbase.coprocessor.example; 019 020import java.io.IOException; 021import java.util.Optional; 022import org.apache.hadoop.hbase.CoprocessorEnvironment; 023import org.apache.hadoop.hbase.TableName; 024import org.apache.hadoop.hbase.client.RegionInfo; 025import org.apache.hadoop.hbase.client.TableDescriptor; 026import org.apache.hadoop.hbase.coprocessor.MasterCoprocessor; 027import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment; 028import org.apache.hadoop.hbase.coprocessor.MasterObserver; 029import org.apache.hadoop.hbase.coprocessor.ObserverContext; 030import org.apache.hadoop.hbase.metrics.Counter; 031import org.apache.hadoop.hbase.metrics.Gauge; 032import org.apache.hadoop.hbase.metrics.MetricRegistry; 033import org.apache.hadoop.hbase.metrics.Timer; 034import org.apache.hadoop.hbase.util.EnvironmentEdgeManager; 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 * <p> 043 * These metrics will be available through the regular Hadoop metrics2 sinks (ganglia, opentsdb, 044 * etc) as well as JMX output. You can view a snapshot of the metrics by going to the http web UI of 045 * the master page, something like http://mymasterhost:16010/jmx 046 * </p> 047 * @see ExampleRegionObserverWithMetrics 048 */ 049@InterfaceAudience.Private 050public class ExampleMasterObserverWithMetrics implements MasterCoprocessor, MasterObserver { 051 @Override 052 public Optional<MasterObserver> getMasterObserver() { 053 return Optional.of(this); 054 } 055 056 private static final Logger LOG = LoggerFactory.getLogger(ExampleMasterObserverWithMetrics.class); 057 058 /** This is the Timer metric object to keep track of the current count across invocations */ 059 private Timer createTableTimer; 060 private long createTableStartTime = Long.MIN_VALUE; 061 062 /** This is a Counter object to keep track of disableTable operations */ 063 private Counter disableTableCounter; 064 065 /** Returns the total memory of the process. We will use this to define a gauge metric */ 066 private long getTotalMemory() { 067 return Runtime.getRuntime().totalMemory(); 068 } 069 070 /** Returns the max memory of the process. We will use this to define a gauge metric */ 071 private long getMaxMemory() { 072 return Runtime.getRuntime().maxMemory(); 073 } 074 075 @Override 076 public void preCreateTable(ObserverContext<MasterCoprocessorEnvironment> ctx, 077 TableDescriptor desc, RegionInfo[] regions) throws IOException { 078 // we rely on the fact that there is only 1 instance of our MasterObserver. We keep track of 079 // when the operation starts before the operation is executing. 080 this.createTableStartTime = EnvironmentEdgeManager.currentTime(); 081 } 082 083 @Override 084 public void postCreateTable(ObserverContext<MasterCoprocessorEnvironment> ctx, 085 TableDescriptor desc, RegionInfo[] regions) throws IOException { 086 if (this.createTableStartTime > 0) { 087 long time = EnvironmentEdgeManager.currentTime() - this.createTableStartTime; 088 LOG.info("Create table took: " + time); 089 090 // Update the timer metric for the create table operation duration. 091 createTableTimer.updateMillis(time); 092 } 093 } 094 095 @Override 096 public void preDisableTable(ObserverContext<MasterCoprocessorEnvironment> ctx, 097 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 = ((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 using Java-8 lambdas (Supplier converted into Gauge) 139 registry.register("maxMemory", this::getMaxMemory); 140 } 141 } 142}