hbase-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From e...@apache.org
Subject [1/3] hbase git commit: HBASE-9774 HBase native metrics and metric collection for coprocessors
Date Wed, 25 Jan 2017 19:48:43 GMT
Repository: hbase
Updated Branches:
  refs/heads/master 59fd6eb7f -> c64a1d199


http://git-wip-us.apache.org/repos/asf/hbase/blob/c64a1d19/hbase-metrics/src/test/java/org/apache/hadoop/hbase/metrics/impl/TestFastLongHistogram.java
----------------------------------------------------------------------
diff --git a/hbase-metrics/src/test/java/org/apache/hadoop/hbase/metrics/impl/TestFastLongHistogram.java b/hbase-metrics/src/test/java/org/apache/hadoop/hbase/metrics/impl/TestFastLongHistogram.java
new file mode 100644
index 0000000..6024d25
--- /dev/null
+++ b/hbase-metrics/src/test/java/org/apache/hadoop/hbase/metrics/impl/TestFastLongHistogram.java
@@ -0,0 +1,132 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.hbase.metrics.impl;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.Arrays;
+import java.util.Random;
+
+import org.apache.hadoop.hbase.testclassification.MiscTests;
+import org.apache.hadoop.hbase.testclassification.SmallTests;
+import org.junit.Assert;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+/**
+ * Testcases for FastLongHistogram.
+ */
+@Category({MiscTests.class, SmallTests.class})
+public class TestFastLongHistogram {
+
+  private static void doTestUniform(FastLongHistogram hist) {
+    long[] VALUES = { 0, 10, 20, 30, 40, 50 };
+    double[] qs = new double[VALUES.length];
+    for (int i = 0; i < qs.length; i++) {
+      qs[i] = (double) VALUES[i] / VALUES[VALUES.length - 1];
+    }
+
+    for (int i = 0; i < 10; i++) {
+      for (long v : VALUES) {
+        hist.add(v, 1);
+      }
+      long[] vals = hist.getQuantiles(qs);
+      System.out.println(Arrays.toString(vals));
+      for (int j = 0; j < qs.length; j++) {
+        Assert.assertTrue(j + "-th element org: " + VALUES[j] + ", act: " + vals[j],
+          Math.abs(vals[j] - VALUES[j]) <= 10);
+      }
+      hist.snapshotAndReset();
+    }
+  }
+
+  @Test
+  public void testUniform() {
+    FastLongHistogram hist = new FastLongHistogram(100, 0, 50);
+    doTestUniform(hist);
+  }
+
+  @Test
+  public void testAdaptionOfChange() {
+    // assumes the uniform distribution
+    FastLongHistogram hist = new FastLongHistogram(100, 0, 100);
+
+    Random rand = new Random();
+
+    for (int n = 0; n < 10; n++) {
+      for (int i = 0; i < 900; i++) {
+        hist.add(rand.nextInt(100), 1);
+      }
+
+      // add 10% outliers, this breaks the assumption, hope bin10xMax works
+      for (int i = 0; i < 100; i++) {
+        hist.add(1000 + rand.nextInt(100), 1);
+      }
+
+      long[] vals = hist.getQuantiles(new double[] { 0.25, 0.75, 0.95 });
+      System.out.println(Arrays.toString(vals));
+      if (n == 0) {
+        Assert.assertTrue("Out of possible value", vals[0] >= 0 && vals[0] <= 50);
+        Assert.assertTrue("Out of possible value", vals[1] >= 50 && vals[1] <= 100);
+        Assert.assertTrue("Out of possible value", vals[2] >= 900 && vals[2] <= 1100);
+      }
+
+      hist.snapshotAndReset();
+    }
+  }
+
+
+  @Test
+  public void testGetNumAtOrBelow() {
+    long[] VALUES = { 1, 10, 20, 30, 40, 50 };
+
+    FastLongHistogram h = new FastLongHistogram();
+    for (long v : VALUES) {
+      for (int i = 0; i < 100; i++) {
+        h.add(v, 1);
+      }
+    }
+
+    h.add(Integer.MAX_VALUE, 1);
+
+    h.snapshotAndReset();
+
+    for (long v : VALUES) {
+      for (int i = 0; i < 100; i++) {
+        h.add(v, 1);
+      }
+    }
+    // Add something way out there to make sure it doesn't throw off the counts.
+    h.add(Integer.MAX_VALUE, 1);
+
+    assertEquals(100, h.getNumAtOrBelow(1));
+    assertEquals(200, h.getNumAtOrBelow(11));
+    assertEquals(601, h.getNumAtOrBelow(Long.MAX_VALUE));
+  }
+
+
+  @Test
+  public void testSameValues() {
+    FastLongHistogram hist = new FastLongHistogram(100);
+
+    hist.add(50, 100);
+
+    hist.snapshotAndReset();
+    doTestUniform(hist);
+  }
+}

http://git-wip-us.apache.org/repos/asf/hbase/blob/c64a1d19/hbase-metrics/src/test/java/org/apache/hadoop/hbase/metrics/impl/TestGauge.java
----------------------------------------------------------------------
diff --git a/hbase-metrics/src/test/java/org/apache/hadoop/hbase/metrics/impl/TestGauge.java b/hbase-metrics/src/test/java/org/apache/hadoop/hbase/metrics/impl/TestGauge.java
new file mode 100644
index 0000000..7927110
--- /dev/null
+++ b/hbase-metrics/src/test/java/org/apache/hadoop/hbase/metrics/impl/TestGauge.java
@@ -0,0 +1,61 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.hbase.metrics.impl;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.concurrent.atomic.AtomicLong;
+
+import org.apache.hadoop.hbase.metrics.Gauge;
+import org.apache.hadoop.hbase.testclassification.SmallTests;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+/**
+ * Test class for {@link Gauge}.
+ */
+@Category(SmallTests.class)
+public class TestGauge {
+
+  @Test
+  public void testGetValue() {
+    SimpleGauge gauge = new SimpleGauge();
+
+    assertEquals(0, (long)gauge.getValue());
+
+    gauge.setValue(1000L);
+
+    assertEquals(1000L, (long)gauge.getValue());
+  }
+
+  /**
+   * Gauge implementation with a setter.
+   */
+  private static class SimpleGauge implements Gauge<Long> {
+
+    private final AtomicLong value = new AtomicLong(0L);
+
+    @Override public Long getValue() {
+      return this.value.get();
+    }
+
+    public void setValue(long value) {
+      this.value.set(value);
+    }
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/hbase/blob/c64a1d19/hbase-metrics/src/test/java/org/apache/hadoop/hbase/metrics/impl/TestHistogramImpl.java
----------------------------------------------------------------------
diff --git a/hbase-metrics/src/test/java/org/apache/hadoop/hbase/metrics/impl/TestHistogramImpl.java b/hbase-metrics/src/test/java/org/apache/hadoop/hbase/metrics/impl/TestHistogramImpl.java
new file mode 100644
index 0000000..1ae3c2c
--- /dev/null
+++ b/hbase-metrics/src/test/java/org/apache/hadoop/hbase/metrics/impl/TestHistogramImpl.java
@@ -0,0 +1,100 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.hadoop.hbase.metrics.impl;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.stream.IntStream;
+
+import org.apache.hadoop.hbase.metrics.Snapshot;
+import org.apache.hadoop.hbase.testclassification.SmallTests;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+/**
+ * Test case for {@link HistogramImpl}
+ */
+@Category(SmallTests.class)
+public class TestHistogramImpl {
+
+  @Test
+  public void testUpdate() {
+    HistogramImpl histogram = new HistogramImpl();
+
+    assertEquals(0, histogram.getCount());
+
+    histogram.update(0);
+    assertEquals(1, histogram.getCount());
+
+    histogram.update(10);
+    assertEquals(2, histogram.getCount());
+
+    histogram.update(20);
+    histogram.update(30);
+
+    assertEquals(4, histogram.getCount());
+  }
+
+  @Test
+  public void testSnapshot() {
+    HistogramImpl histogram = new HistogramImpl();
+    IntStream.range(0, 100).forEach(histogram::update);
+
+    Snapshot snapshot = histogram.snapshot();
+
+    assertEquals(100, snapshot.getCount());
+    assertEquals(50, snapshot.getMedian());
+    assertEquals(49, snapshot.getMean());
+    assertEquals(0, snapshot.getMin());
+    assertEquals(99, snapshot.getMax());
+    assertEquals(25, snapshot.get25thPercentile());
+    assertEquals(75, snapshot.get75thPercentile());
+    assertEquals(90, snapshot.get90thPercentile());
+    assertEquals(95, snapshot.get95thPercentile());
+    assertEquals(98, snapshot.get98thPercentile());
+    assertEquals(99, snapshot.get99thPercentile());
+    assertEquals(99, snapshot.get999thPercentile());
+
+    assertEquals(51, snapshot.getCountAtOrBelow(50));
+
+    // check that histogram is reset.
+    assertEquals(100, histogram.getCount()); // count does not reset
+
+    // put more data after reset
+    IntStream.range(100, 200).forEach(histogram::update);
+
+    assertEquals(200, histogram.getCount());
+
+    snapshot = histogram.snapshot();
+    assertEquals(100, snapshot.getCount()); // only 100 more events
+    assertEquals(150, snapshot.getMedian());
+    assertEquals(149, snapshot.getMean());
+    assertEquals(100, snapshot.getMin());
+    assertEquals(199, snapshot.getMax());
+    assertEquals(125, snapshot.get25thPercentile());
+    assertEquals(175, snapshot.get75thPercentile());
+    assertEquals(190, snapshot.get90thPercentile());
+    assertEquals(195, snapshot.get95thPercentile());
+    assertEquals(198, snapshot.get98thPercentile());
+    assertEquals(199, snapshot.get99thPercentile());
+    assertEquals(199, snapshot.get999thPercentile());
+  }
+}

http://git-wip-us.apache.org/repos/asf/hbase/blob/c64a1d19/hbase-metrics/src/test/java/org/apache/hadoop/hbase/metrics/impl/TestMetricRegistryImpl.java
----------------------------------------------------------------------
diff --git a/hbase-metrics/src/test/java/org/apache/hadoop/hbase/metrics/impl/TestMetricRegistryImpl.java b/hbase-metrics/src/test/java/org/apache/hadoop/hbase/metrics/impl/TestMetricRegistryImpl.java
new file mode 100644
index 0000000..bf08617
--- /dev/null
+++ b/hbase-metrics/src/test/java/org/apache/hadoop/hbase/metrics/impl/TestMetricRegistryImpl.java
@@ -0,0 +1,142 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.hadoop.hbase.metrics.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Map;
+import java.util.Optional;
+
+import org.apache.hadoop.hbase.metrics.Counter;
+import org.apache.hadoop.hbase.metrics.Gauge;
+import org.apache.hadoop.hbase.metrics.Meter;
+import org.apache.hadoop.hbase.metrics.Metric;
+import org.apache.hadoop.hbase.metrics.MetricRegistryInfo;
+import org.apache.hadoop.hbase.metrics.Timer;
+import org.apache.hadoop.hbase.testclassification.SmallTests;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+@Category(SmallTests.class)
+public class TestMetricRegistryImpl {
+  private MetricRegistryInfo info;
+  private MetricRegistryImpl registry;
+
+  @Before
+  public void setUp() {
+    info = new MetricRegistryInfo("foo", "bar", "baz", "foobar", false);
+    registry = new MetricRegistryImpl(info);
+  }
+
+  @Test
+  public void testCounter() {
+    Counter counter = registry.counter("mycounter");
+    assertNotNull(counter);
+    counter.increment(42L);
+    Optional<Metric> metric = registry.get("mycounter");
+    assertTrue(metric.isPresent());
+    assertEquals(42L, (long)((Counter)metric.get()).getCount());
+  }
+
+  @Test
+  public void testRegisterGauge() {
+    registry.register("mygauge", new Gauge<Long>() {
+      @Override
+      public Long getValue() {
+        return 42L;
+      }
+    });
+    Optional<Metric> metric = registry.get("mygauge");
+    assertTrue(metric.isPresent());
+    assertEquals(42L, (long)((Gauge<Long>)metric.get()).getValue());
+  }
+
+  @Test
+  public void testRegisterGaugeLambda() {
+    // register a Gauge using lambda expression
+    registry.register("gaugeLambda", () -> 42L);
+    Optional<Metric> metric = registry.get("gaugeLambda");
+    assertTrue(metric.isPresent());
+    assertEquals(42L, (long)((Gauge<Long>)metric.get()).getValue());
+  }
+
+  @Test
+  public void testTimer() {
+    Timer timer = registry.timer("mytimer");
+    assertNotNull(timer);
+    timer.updateNanos(100);
+  }
+
+  @Test
+  public void testMeter() {
+    Meter meter = registry.meter("mymeter");
+    assertNotNull(meter);
+    meter.mark();
+  }
+
+  @Test
+  public void testRegister() {
+    CounterImpl counter = new CounterImpl();
+    registry.register("mycounter", counter);
+    counter.increment(42L);
+
+    Optional<Metric> metric = registry.get("mycounter");
+    assertTrue(metric.isPresent());
+    assertEquals(42L, (long)((Counter)metric.get()).getCount());
+  }
+
+  @Test
+  public void testDoubleRegister() {
+    Gauge g1 = registry.register("mygauge", () -> 42L);
+    Gauge g2 = registry.register("mygauge", () -> 52L);
+
+    // second gauge is ignored if it exists
+    assertEquals(g1, g2);
+
+    Optional<Metric> metric = registry.get("mygauge");
+    assertTrue(metric.isPresent());
+    assertEquals(42L, (long)((Gauge<Long>)metric.get()).getValue());
+
+
+    Counter c1 = registry.counter("mycounter");
+    Counter c2 = registry.counter("mycounter");
+
+    assertEquals(c1, c2);
+  }
+
+  @Test
+  public void testGetMetrics() {
+    CounterImpl counter = new CounterImpl();
+    registry.register("mycounter", counter);
+    Gauge gauge = registry.register("mygauge", () -> 42L);
+    Timer timer = registry.timer("mytimer");
+
+    Map<String, Metric> metrics = registry.getMetrics();
+    assertEquals(3, metrics.size());
+
+    assertEquals(counter, metrics.get("mycounter"));
+    assertEquals(gauge, metrics.get("mygauge"));
+    assertEquals(timer, metrics.get("mytimer"));
+  }
+}

http://git-wip-us.apache.org/repos/asf/hbase/blob/c64a1d19/hbase-metrics/src/test/java/org/apache/hadoop/hbase/metrics/impl/TestRefCountingMap.java
----------------------------------------------------------------------
diff --git a/hbase-metrics/src/test/java/org/apache/hadoop/hbase/metrics/impl/TestRefCountingMap.java b/hbase-metrics/src/test/java/org/apache/hadoop/hbase/metrics/impl/TestRefCountingMap.java
new file mode 100644
index 0000000..cf31d50
--- /dev/null
+++ b/hbase-metrics/src/test/java/org/apache/hadoop/hbase/metrics/impl/TestRefCountingMap.java
@@ -0,0 +1,154 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.hadoop.hbase.metrics.impl;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.util.Collection;
+import java.util.Set;
+
+import org.apache.hadoop.hbase.testclassification.SmallTests;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import com.google.common.collect.Lists;
+
+@Category(SmallTests.class)
+public class TestRefCountingMap {
+
+  private RefCountingMap<String, String> map;
+
+  @Before
+  public void setUp() {
+    map = new RefCountingMap<>();
+  }
+
+  @Test
+  public void testPutGet() {
+    map.put("foo", () -> "foovalue");
+
+    String v = map.get("foo");
+    assertNotNull(v);
+    assertEquals("foovalue", v);
+  }
+
+  @Test
+  public void testPutMulti() {
+    String v1 = map.put("foo", () -> "foovalue");
+    String v2 =  map.put("foo", () -> "foovalue2");
+    String v3 = map.put("foo", () -> "foovalue3");
+
+    String v = map.get("foo");
+    assertEquals("foovalue", v);
+    assertEquals(v, v1);
+    assertEquals(v, v2);
+    assertEquals(v, v3);
+  }
+
+  @Test
+  public void testPutRemove() {
+    map.put("foo", () -> "foovalue");
+    String v = map.remove("foo");
+    assertNull(v);
+    v = map.get("foo");
+    assertNull(v);
+  }
+
+  @Test
+  public void testPutRemoveMulti() {
+    map.put("foo", () -> "foovalue");
+    map.put("foo", () -> "foovalue2");
+    map.put("foo", () -> "foovalue3");
+
+    // remove 1
+    String v = map.remove("foo");
+    assertEquals("foovalue", v);
+
+    // remove 2
+    v = map.remove("foo");
+    assertEquals("foovalue", v);
+
+    // remove 3
+    v = map.remove("foo");
+    assertNull(v);
+    v = map.get("foo");
+    assertNull(v);
+  }
+
+  @Test
+  public void testSize() {
+    assertEquals(0, map.size());
+
+    // put a key
+    map.put("foo", () -> "foovalue");
+    assertEquals(1, map.size());
+
+    // put a different key
+    map.put("bar", () -> "foovalue2");
+    assertEquals(2, map.size());
+
+    // put the same key again
+    map.put("bar", () -> "foovalue3");
+    assertEquals(2, map.size()); // map should be same size
+  }
+
+  @Test
+  public void testClear() {
+    map.put("foo", () -> "foovalue");
+    map.put("bar", () -> "foovalue2");
+    map.put("baz", () -> "foovalue3");
+
+    map.clear();
+
+    assertEquals(0, map.size());
+  }
+
+
+  @Test
+  public void testKeySet() {
+    map.put("foo", () -> "foovalue");
+    map.put("bar", () -> "foovalue2");
+    map.put("baz", () -> "foovalue3");
+
+    Set<String> keys = map.keySet();
+    assertEquals(3, keys.size());
+
+    Lists.newArrayList("foo", "bar", "baz").stream().forEach( v -> assertTrue(keys.contains(v)));
+  }
+
+  @Test
+  public void testValues() {
+    map.put("foo", () -> "foovalue");
+    map.put("foo", () -> "foovalue2");
+    map.put("bar", () -> "foovalue3");
+    map.put("baz", () -> "foovalue4");
+
+    Collection<String> values = map.values();
+    assertEquals(3, values.size());
+
+    Lists.newArrayList("foovalue", "foovalue3", "foovalue4").stream().forEach(
+        v -> assertTrue(values.contains(v)));
+  }
+}

http://git-wip-us.apache.org/repos/asf/hbase/blob/c64a1d19/hbase-metrics/src/test/java/org/apache/hadoop/hbase/metrics/impl/TestTimerImpl.java
----------------------------------------------------------------------
diff --git a/hbase-metrics/src/test/java/org/apache/hadoop/hbase/metrics/impl/TestTimerImpl.java b/hbase-metrics/src/test/java/org/apache/hadoop/hbase/metrics/impl/TestTimerImpl.java
new file mode 100644
index 0000000..2b3dc8f
--- /dev/null
+++ b/hbase-metrics/src/test/java/org/apache/hadoop/hbase/metrics/impl/TestTimerImpl.java
@@ -0,0 +1,53 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.hadoop.hbase.metrics.impl;
+
+import static org.junit.Assert.assertEquals;
+
+import java.util.concurrent.TimeUnit;
+
+import org.apache.hadoop.hbase.metrics.Timer;
+import org.apache.hadoop.hbase.testclassification.SmallTests;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+/**
+ * Test class for {@link TimerImpl}
+ */
+@Category(SmallTests.class)
+public class TestTimerImpl {
+
+  private Timer timer;
+
+  @Before
+  public void setup() {
+    this.timer = new TimerImpl();
+  }
+
+  @Test
+  public void testUpdate() {
+    timer.update(40, TimeUnit.MILLISECONDS);
+    timer.update(41, TimeUnit.MILLISECONDS);
+    timer.update(42, TimeUnit.MILLISECONDS);
+    assertEquals(3, timer.getHistogram().getCount());
+    assertEquals(3, timer.getMeter().getCount());
+
+    assertEquals(TimeUnit.MILLISECONDS.toMicros(41), timer.getHistogram().snapshot().getMedian());
+  }
+}
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/hbase/blob/c64a1d19/hbase-server/pom.xml
----------------------------------------------------------------------
diff --git a/hbase-server/pom.xml b/hbase-server/pom.xml
index c46ff69..09a4512 100644
--- a/hbase-server/pom.xml
+++ b/hbase-server/pom.xml
@@ -387,6 +387,14 @@
       <type>test-jar</type>
       <scope>test</scope>
     </dependency>
+    <dependency>
+      <groupId>org.apache.hbase</groupId>
+      <artifactId>hbase-metrics-api</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.hbase</groupId>
+      <artifactId>hbase-metrics</artifactId>
+    </dependency>
     <!-- resource bundle only needed at build time -->
     <dependency>
        <groupId>org.apache.hbase</groupId>

http://git-wip-us.apache.org/repos/asf/hbase/blob/c64a1d19/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterCoprocessorEnvironment.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterCoprocessorEnvironment.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterCoprocessorEnvironment.java
index abe934a..a6e54e0 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterCoprocessorEnvironment.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MasterCoprocessorEnvironment.java
@@ -24,10 +24,20 @@ import org.apache.hadoop.hbase.classification.InterfaceStability;
 import org.apache.hadoop.hbase.CoprocessorEnvironment;
 import org.apache.hadoop.hbase.HBaseInterfaceAudience;
 import org.apache.hadoop.hbase.master.MasterServices;
+import org.apache.hadoop.hbase.metrics.MetricRegistry;
 
 @InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.COPROC)
 @InterfaceStability.Evolving
 public interface MasterCoprocessorEnvironment extends CoprocessorEnvironment {
   /** @return reference to the HMaster services */
   MasterServices getMasterServices();
+
+  /**
+   * Returns a MetricRegistry that can be used to track metrics at the master level.
+   *
+   * <p>See ExampleMasterObserverWithMetrics class in the hbase-examples modules for examples
+   * of how metrics can be instantiated and used.</p>
+   * @return A MetricRegistry for the coprocessor class to track and export metrics.
+   */
+  MetricRegistry getMetricRegistryForMaster();
 }

http://git-wip-us.apache.org/repos/asf/hbase/blob/c64a1d19/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MetricsCoprocessor.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MetricsCoprocessor.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MetricsCoprocessor.java
new file mode 100644
index 0000000..d564002
--- /dev/null
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/MetricsCoprocessor.java
@@ -0,0 +1,136 @@
+/**
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package org.apache.hadoop.hbase.coprocessor;
+
+import org.apache.hadoop.hbase.classification.InterfaceAudience;
+import org.apache.hadoop.hbase.metrics.MetricRegistries;
+import org.apache.hadoop.hbase.metrics.MetricRegistry;
+import org.apache.hadoop.hbase.metrics.MetricRegistryInfo;
+
+import com.google.common.annotations.VisibleForTesting;
+
+/**
+ * Utility class for tracking metrics for various types of coprocessors. Each coprocessor instance
+ * creates its own MetricRegistry which is exported as an individual MetricSource. MetricRegistries
+ * are ref counted using the hbase-metric module interfaces.
+ */
+@InterfaceAudience.Private
+public class MetricsCoprocessor {
+
+  // Master coprocessor metrics
+  private static final String MASTER_COPROC_METRICS_NAME = "Coprocessor.Master";
+  private static final String MASTER_COPROC_METRICS_CONTEXT = "master";
+  private static final String MASTER_COPROC_METRICS_DESCRIPTION
+      = "Metrics about HBase MasterObservers";
+  private static final String MASTER_COPROC_METRICS_JMX_CONTEXT
+      = "Master,sub=" + MASTER_COPROC_METRICS_NAME;
+
+  // RegionServer coprocessor metrics
+  private static final String RS_COPROC_METRICS_NAME = "Coprocessor.RegionServer";
+  private static final String RS_COPROC_METRICS_CONTEXT = "regionserver";
+  private static final String RS_COPROC_METRICS_DESCRIPTION
+      = "Metrics about HBase RegionServerObservers";
+  private static final String RS_COPROC_METRICS_JMX_CONTEXT = "RegionServer,sub="
+      + RS_COPROC_METRICS_NAME;
+
+  // Region coprocessor metrics
+  private static final String REGION_COPROC_METRICS_NAME = "Coprocessor.Region";
+  private static final String REGION_COPROC_METRICS_CONTEXT = "regionserver";
+  private static final String REGION_COPROC_METRICS_DESCRIPTION
+      = "Metrics about HBase RegionObservers";
+  private static final String REGION_COPROC_METRICS_JMX_CONTEXT
+      = "RegionServer,sub=" + REGION_COPROC_METRICS_NAME;
+
+  // WAL coprocessor metrics
+  private static final String WAL_COPROC_METRICS_NAME = "Coprocessor.WAL";
+  private static final String WAL_COPROC_METRICS_CONTEXT = "regionserver";
+  private static final String WAL_COPROC_METRICS_DESCRIPTION
+      = "Metrics about HBase WALObservers";
+  private static final String WAL_COPROC_METRICS_JMX_CONTEXT
+      = "RegionServer,sub=" + WAL_COPROC_METRICS_NAME;
+
+  private static String suffix(String metricName, String cpName) {
+    return new StringBuilder(metricName)
+        .append(".")
+        .append("CP_")
+        .append(cpName)
+        .toString();
+  }
+
+  @VisibleForTesting
+  static MetricRegistryInfo createRegistryInfoForMasterCoprocessor(String clazz) {
+    return new MetricRegistryInfo(
+        suffix(MASTER_COPROC_METRICS_NAME, clazz),
+        MASTER_COPROC_METRICS_DESCRIPTION,
+        suffix(MASTER_COPROC_METRICS_JMX_CONTEXT, clazz),
+        MASTER_COPROC_METRICS_CONTEXT, false);
+  }
+
+  public static MetricRegistry createRegistryForMasterCoprocessor(String clazz) {
+    return MetricRegistries.global().create(createRegistryInfoForMasterCoprocessor(clazz));
+  }
+
+  @VisibleForTesting
+  static MetricRegistryInfo createRegistryInfoForRSCoprocessor(String clazz) {
+    return new MetricRegistryInfo(
+        suffix(RS_COPROC_METRICS_NAME, clazz),
+        RS_COPROC_METRICS_DESCRIPTION,
+        suffix(RS_COPROC_METRICS_JMX_CONTEXT, clazz),
+        RS_COPROC_METRICS_CONTEXT, false);
+  }
+
+  public static MetricRegistry createRegistryForRSCoprocessor(String clazz) {
+    return MetricRegistries.global().create(createRegistryInfoForRSCoprocessor(clazz));
+  }
+
+  @VisibleForTesting
+  public static MetricRegistryInfo createRegistryInfoForRegionCoprocessor(String clazz) {
+    return new MetricRegistryInfo(
+        suffix(REGION_COPROC_METRICS_NAME, clazz),
+        REGION_COPROC_METRICS_DESCRIPTION,
+        suffix(REGION_COPROC_METRICS_JMX_CONTEXT, clazz),
+        REGION_COPROC_METRICS_CONTEXT, false);
+  }
+
+  public static MetricRegistry createRegistryForRegionCoprocessor(String clazz) {
+    return MetricRegistries.global().create(createRegistryInfoForRegionCoprocessor(clazz));
+  }
+
+  @VisibleForTesting
+  public static MetricRegistryInfo createRegistryInfoForWALCoprocessor(String clazz) {
+    return new MetricRegistryInfo(
+        suffix(WAL_COPROC_METRICS_NAME, clazz),
+        WAL_COPROC_METRICS_DESCRIPTION,
+        suffix(WAL_COPROC_METRICS_JMX_CONTEXT, clazz),
+        WAL_COPROC_METRICS_CONTEXT, false);
+  }
+
+  public static MetricRegistry createRegistryForWALCoprocessor(String clazz) {
+    return MetricRegistries.global().create(createRegistryInfoForWALCoprocessor(clazz));
+  }
+
+  public static void removeRegistry(MetricRegistry registry) {
+    if (registry == null) {
+      return;
+    }
+    MetricRegistries.global().remove(registry.getMetricRegistryInfo());
+  }
+}

http://git-wip-us.apache.org/repos/asf/hbase/blob/c64a1d19/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionCoprocessorEnvironment.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionCoprocessorEnvironment.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionCoprocessorEnvironment.java
index bdf88af..3566f06 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionCoprocessorEnvironment.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionCoprocessorEnvironment.java
@@ -21,11 +21,12 @@ package org.apache.hadoop.hbase.coprocessor;
 
 import java.util.concurrent.ConcurrentMap;
 
-import org.apache.hadoop.hbase.classification.InterfaceAudience;
-import org.apache.hadoop.hbase.classification.InterfaceStability;
 import org.apache.hadoop.hbase.CoprocessorEnvironment;
 import org.apache.hadoop.hbase.HBaseInterfaceAudience;
 import org.apache.hadoop.hbase.HRegionInfo;
+import org.apache.hadoop.hbase.classification.InterfaceAudience;
+import org.apache.hadoop.hbase.classification.InterfaceStability;
+import org.apache.hadoop.hbase.metrics.MetricRegistry;
 import org.apache.hadoop.hbase.regionserver.Region;
 import org.apache.hadoop.hbase.regionserver.RegionServerServices;
 
@@ -43,4 +44,23 @@ public interface RegionCoprocessorEnvironment extends CoprocessorEnvironment {
 
   /** @return shared data between all instances of this coprocessor */
   ConcurrentMap<String, Object> getSharedData();
+
+  /**
+   * Returns a MetricRegistry that can be used to track metrics at the region server level. All
+   * metrics tracked at this level will be shared by all the coprocessor instances
+   * of the same class in the same region server process. Note that there will be one
+   * region coprocessor environment per region in the server, but all of these instances will share
+   * the same MetricRegistry. The metric instances (like Counter, Timer, etc) will also be shared
+   * among all of the region coprocessor instances.
+   *
+   * <p>See ExampleRegionObserverWithMetrics class in the hbase-examples modules to see examples of how
+   * metrics can be instantiated and used.</p>
+   * @return A MetricRegistry for the coprocessor class to track and export metrics.
+   */
+  // Note: we are not exposing getMetricRegistryForRegion(). per-region metrics are already costly
+  // so we do not want to allow coprocessors to export metrics at the region level. We can allow
+  // getMetricRegistryForTable() to allow coprocessors to track metrics per-table, per-regionserver.
+  MetricRegistry getMetricRegistryForRegionServer();
+
+
 }

http://git-wip-us.apache.org/repos/asf/hbase/blob/c64a1d19/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionServerCoprocessorEnvironment.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionServerCoprocessorEnvironment.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionServerCoprocessorEnvironment.java
index 527df45..f42556a 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionServerCoprocessorEnvironment.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/RegionServerCoprocessorEnvironment.java
@@ -19,6 +19,7 @@
 package org.apache.hadoop.hbase.coprocessor;
 
 import org.apache.hadoop.hbase.CoprocessorEnvironment;
+import org.apache.hadoop.hbase.metrics.MetricRegistry;
 import org.apache.hadoop.hbase.regionserver.RegionServerServices;
 
 public interface RegionServerCoprocessorEnvironment extends CoprocessorEnvironment {
@@ -28,4 +29,13 @@ public interface RegionServerCoprocessorEnvironment extends CoprocessorEnvironme
    * @return the region server services
    */
   RegionServerServices getRegionServerServices();
+
+  /**
+   * Returns a MetricRegistry that can be used to track metrics at the region server level.
+   *
+   * <p>See ExampleMasterObserverWithMetrics class in the hbase-examples modules for examples
+   * of how metrics can be instantiated and used.</p>
+   * @return A MetricRegistry for the coprocessor class to track and export metrics.
+   */
+  MetricRegistry getMetricRegistryForRegionServer();
 }

http://git-wip-us.apache.org/repos/asf/hbase/blob/c64a1d19/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/WALCoprocessorEnvironment.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/WALCoprocessorEnvironment.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/WALCoprocessorEnvironment.java
index a4ce5f1..0865d96 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/WALCoprocessorEnvironment.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/coprocessor/WALCoprocessorEnvironment.java
@@ -23,6 +23,7 @@ import org.apache.hadoop.hbase.classification.InterfaceAudience;
 import org.apache.hadoop.hbase.classification.InterfaceStability;
 import org.apache.hadoop.hbase.CoprocessorEnvironment;
 import org.apache.hadoop.hbase.HBaseInterfaceAudience;
+import org.apache.hadoop.hbase.metrics.MetricRegistry;
 import org.apache.hadoop.hbase.wal.WAL;
 
 @InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.COPROC)
@@ -30,4 +31,13 @@ import org.apache.hadoop.hbase.wal.WAL;
 public interface WALCoprocessorEnvironment extends CoprocessorEnvironment {
   /** @return reference to the region server's WAL */
   WAL getWAL();
+
+  /**
+   * Returns a MetricRegistry that can be used to track metrics at the region server level.
+   *
+   * <p>See ExampleRegionServerObserverWithMetrics class in the hbase-examples modules for examples
+   * of how metrics can be instantiated and used.</p>
+   * @return A MetricRegistry for the coprocessor class to track and export metrics.
+   */
+  MetricRegistry getMetricRegistryForRegionServer();
 }

http://git-wip-us.apache.org/repos/asf/hbase/blob/c64a1d19/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/AgeSnapshot.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/AgeSnapshot.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/AgeSnapshot.java
index 4c1ad23..dd3bf25 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/AgeSnapshot.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/AgeSnapshot.java
@@ -17,7 +17,7 @@
  */
 package org.apache.hadoop.hbase.io.hfile;
 
-import org.apache.hadoop.hbase.util.FastLongHistogram;
+import org.apache.hadoop.hbase.metrics.impl.FastLongHistogram;
 import org.codehaus.jackson.annotate.JsonIgnoreProperties;
 
 /**

http://git-wip-us.apache.org/repos/asf/hbase/blob/c64a1d19/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/BlockCacheUtil.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/BlockCacheUtil.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/BlockCacheUtil.java
index 3c11149..9335ef6 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/BlockCacheUtil.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/BlockCacheUtil.java
@@ -25,7 +25,7 @@ import java.util.concurrent.ConcurrentSkipListSet;
 
 import org.apache.hadoop.hbase.classification.InterfaceAudience;
 import org.apache.hadoop.conf.Configuration;
-import org.apache.hadoop.hbase.util.FastLongHistogram;
+import org.apache.hadoop.hbase.metrics.impl.FastLongHistogram;
 import org.codehaus.jackson.JsonGenerationException;
 import org.codehaus.jackson.annotate.JsonIgnoreProperties;
 import org.codehaus.jackson.map.JsonMappingException;

http://git-wip-us.apache.org/repos/asf/hbase/blob/c64a1d19/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/CacheStats.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/CacheStats.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/CacheStats.java
index 7264eda..76e6d2a 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/CacheStats.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/io/hfile/CacheStats.java
@@ -23,7 +23,7 @@ import java.util.concurrent.atomic.AtomicLong;
 import java.util.concurrent.atomic.LongAdder;
 
 import org.apache.hadoop.hbase.classification.InterfaceAudience;
-import org.apache.hadoop.hbase.util.FastLongHistogram;
+import org.apache.hadoop.hbase.metrics.impl.FastLongHistogram;
 
 
 /**

http://git-wip-us.apache.org/repos/asf/hbase/blob/c64a1d19/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterCoprocessorHost.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterCoprocessorHost.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterCoprocessorHost.java
index 3dec2e8..28a8550 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterCoprocessorHost.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/master/MasterCoprocessorHost.java
@@ -19,8 +19,6 @@
 
 package org.apache.hadoop.hbase.master;
 
-import com.google.common.net.HostAndPort;
-
 import java.io.IOException;
 import java.util.List;
 import java.util.Set;
@@ -45,15 +43,19 @@ import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
 import org.apache.hadoop.hbase.coprocessor.CoprocessorService;
 import org.apache.hadoop.hbase.coprocessor.MasterCoprocessorEnvironment;
 import org.apache.hadoop.hbase.coprocessor.MasterObserver;
+import org.apache.hadoop.hbase.coprocessor.MetricsCoprocessor;
 import org.apache.hadoop.hbase.coprocessor.ObserverContext;
 import org.apache.hadoop.hbase.ipc.RpcServer;
 import org.apache.hadoop.hbase.master.locking.LockProcedure;
 import org.apache.hadoop.hbase.master.procedure.MasterProcedureEnv;
+import org.apache.hadoop.hbase.metrics.MetricRegistry;
 import org.apache.hadoop.hbase.procedure2.ProcedureExecutor;
 import org.apache.hadoop.hbase.replication.ReplicationPeerConfig;
+import org.apache.hadoop.hbase.security.User;
 import org.apache.hadoop.hbase.shaded.protobuf.generated.HBaseProtos.SnapshotDescription;
 import org.apache.hadoop.hbase.shaded.protobuf.generated.QuotaProtos.Quotas;
-import org.apache.hadoop.hbase.security.User;
+
+import com.google.common.net.HostAndPort;
 
 /**
  * Provides the coprocessor framework and environment for master oriented
@@ -72,8 +74,9 @@ public class MasterCoprocessorHost
    */
   static class MasterEnvironment extends CoprocessorHost.Environment
       implements MasterCoprocessorEnvironment {
-    private MasterServices masterServices;
-    final boolean supportGroupCPs;
+    private final MasterServices masterServices;
+    private final boolean supportGroupCPs;
+    private final MetricRegistry metricRegistry;
 
     public MasterEnvironment(final Class<?> implClass, final Coprocessor impl,
         final int priority, final int seq, final Configuration conf,
@@ -82,11 +85,25 @@ public class MasterCoprocessorHost
       this.masterServices = services;
       supportGroupCPs = !useLegacyMethod(impl.getClass(),
           "preBalanceRSGroup", ObserverContext.class, String.class);
+      this.metricRegistry =
+          MetricsCoprocessor.createRegistryForMasterCoprocessor(implClass.getName());
     }
 
+    @Override
     public MasterServices getMasterServices() {
       return masterServices;
     }
+
+    @Override
+    public MetricRegistry getMetricRegistryForMaster() {
+      return metricRegistry;
+    }
+
+    @Override
+    protected void shutdown() {
+      super.shutdown();
+      MetricsCoprocessor.removeRegistry(this.metricRegistry);
+    }
   }
 
   private MasterServices masterServices;

http://git-wip-us.apache.org/repos/asf/hbase/blob/c64a1d19/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/MetricsRegionServer.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/MetricsRegionServer.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/MetricsRegionServer.java
index 8bca6c5..4c28763 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/MetricsRegionServer.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/MetricsRegionServer.java
@@ -17,10 +17,14 @@
  */
 package org.apache.hadoop.hbase.regionserver;
 
-import com.google.common.annotations.VisibleForTesting;
+import org.apache.hadoop.hbase.CompatibilitySingletonFactory;
 import org.apache.hadoop.hbase.classification.InterfaceAudience;
 import org.apache.hadoop.hbase.classification.InterfaceStability;
-import org.apache.hadoop.hbase.CompatibilitySingletonFactory;
+import org.apache.hadoop.hbase.metrics.MetricRegistries;
+import org.apache.hadoop.hbase.metrics.MetricRegistry;
+import org.apache.hadoop.hbase.metrics.Timer;
+
+import com.google.common.annotations.VisibleForTesting;
 
 /**
  * <p>
@@ -36,11 +40,20 @@ public class MetricsRegionServer {
   private MetricsRegionServerSource serverSource;
   private MetricsRegionServerWrapper regionServerWrapper;
 
+  private MetricRegistry metricRegistry;
+  private Timer bulkLoadTimer;
+
   public MetricsRegionServer(MetricsRegionServerWrapper regionServerWrapper) {
     this(regionServerWrapper,
         CompatibilitySingletonFactory.getInstance(MetricsRegionServerSourceFactory.class)
             .createServer(regionServerWrapper));
 
+    // Create hbase-metrics module based metrics. The registry should already be registered by the
+    // MetricsRegionServerSource
+    metricRegistry = MetricRegistries.global().get(serverSource.getMetricRegistryInfo()).get();
+
+    // create and use metrics from the new hbase-metrics based registry.
+    bulkLoadTimer = metricRegistry.timer("Bulkload");
   }
 
   MetricsRegionServer(MetricsRegionServerWrapper regionServerWrapper,
@@ -131,4 +144,8 @@ public class MetricsRegionServer {
     serverSource.updateCompactionInputSize(isMajor, inputBytes);
     serverSource.updateCompactionOutputSize(isMajor, outputBytes);
   }
+
+  public void updateBulkLoad(long millis) {
+    this.bulkLoadTimer.updateMillis(millis);
+  }
 }

http://git-wip-us.apache.org/repos/asf/hbase/blob/c64a1d19/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RSRpcServices.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RSRpcServices.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RSRpcServices.java
index 9847dfe..7307372 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RSRpcServices.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RSRpcServices.java
@@ -208,6 +208,8 @@ import org.apache.hadoop.hbase.wal.WALSplitter;
 import org.apache.hadoop.hbase.zookeeper.ZKSplitLog;
 import org.apache.zookeeper.KeeperException;
 
+import com.google.common.annotations.VisibleForTesting;
+
 /**
  * Implements the regionserver RPC services.
  */
@@ -2113,6 +2115,7 @@ public class RSRpcServices implements HBaseRPCErrorHandler,
   @Override
   public BulkLoadHFileResponse bulkLoadHFile(final RpcController controller,
       final BulkLoadHFileRequest request) throws ServiceException {
+    long start = EnvironmentEdgeManager.currentTime();
     try {
       checkOpen();
       requestCount.increment();
@@ -2157,6 +2160,11 @@ public class RSRpcServices implements HBaseRPCErrorHandler,
       return builder.build();
     } catch (IOException ie) {
       throw new ServiceException(ie);
+    } finally {
+      if (regionServer.metricsRegionServer != null) {
+        regionServer.metricsRegionServer.updateBulkLoad(
+            EnvironmentEdgeManager.currentTime() - start);
+      }
     }
   }
 

http://git-wip-us.apache.org/repos/asf/hbase/blob/c64a1d19/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionCoprocessorHost.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionCoprocessorHost.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionCoprocessorHost.java
index 94508b6..e4b47bc 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionCoprocessorHost.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionCoprocessorHost.java
@@ -19,11 +19,6 @@
 
 package org.apache.hadoop.hbase.regionserver;
 
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-import com.google.protobuf.Message;
-import com.google.protobuf.Service;
-
 import java.io.IOException;
 import java.util.ArrayList;
 import java.util.List;
@@ -44,7 +39,6 @@ import org.apache.hadoop.fs.FileSystem;
 import org.apache.hadoop.fs.Path;
 import org.apache.hadoop.hbase.Cell;
 import org.apache.hadoop.hbase.Coprocessor;
-import org.apache.hadoop.hbase.CoprocessorEnvironment;
 import org.apache.hadoop.hbase.HBaseConfiguration;
 import org.apache.hadoop.hbase.HBaseInterfaceAudience;
 import org.apache.hadoop.hbase.HConstants;
@@ -65,6 +59,7 @@ import org.apache.hadoop.hbase.coprocessor.BaseRegionObserver;
 import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
 import org.apache.hadoop.hbase.coprocessor.CoprocessorService;
 import org.apache.hadoop.hbase.coprocessor.EndpointObserver;
+import org.apache.hadoop.hbase.coprocessor.MetricsCoprocessor;
 import org.apache.hadoop.hbase.coprocessor.ObserverContext;
 import org.apache.hadoop.hbase.coprocessor.RegionCoprocessorEnvironment;
 import org.apache.hadoop.hbase.coprocessor.RegionObserver;
@@ -75,6 +70,7 @@ import org.apache.hadoop.hbase.io.FSDataInputStreamWrapper;
 import org.apache.hadoop.hbase.io.Reference;
 import org.apache.hadoop.hbase.io.hfile.CacheConfig;
 import org.apache.hadoop.hbase.ipc.RpcServer;
+import org.apache.hadoop.hbase.metrics.MetricRegistry;
 import org.apache.hadoop.hbase.regionserver.Region.Operation;
 import org.apache.hadoop.hbase.regionserver.compactions.CompactionRequest;
 import org.apache.hadoop.hbase.regionserver.querymatcher.DeleteTracker;
@@ -85,6 +81,11 @@ import org.apache.hadoop.hbase.util.CoprocessorClassLoader;
 import org.apache.hadoop.hbase.util.Pair;
 import org.apache.hadoop.hbase.wal.WALKey;
 
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+import com.google.protobuf.Message;
+import com.google.protobuf.Service;
+
 /**
  * Implements the coprocessor environment and runtime support for coprocessors
  * loaded within a {@link Region}.
@@ -112,6 +113,7 @@ public class RegionCoprocessorHost
     private Region region;
     private RegionServerServices rsServices;
     ConcurrentMap<String, Object> sharedData;
+    private final MetricRegistry metricRegistry;
 
     /**
      * Constructor
@@ -125,6 +127,8 @@ public class RegionCoprocessorHost
       this.region = region;
       this.rsServices = services;
       this.sharedData = sharedData;
+      this.metricRegistry =
+          MetricsCoprocessor.createRegistryForRegionCoprocessor(impl.getClass().getName());
     }
 
     /** @return the region */
@@ -141,6 +145,7 @@ public class RegionCoprocessorHost
 
     public void shutdown() {
       super.shutdown();
+      MetricsCoprocessor.removeRegistry(this.metricRegistry);
     }
 
     @Override
@@ -153,6 +158,10 @@ public class RegionCoprocessorHost
       return region.getRegionInfo();
     }
 
+    @Override
+    public MetricRegistry getMetricRegistryForRegionServer() {
+      return metricRegistry;
+    }
   }
 
   static class TableCoprocessorAttribute {

http://git-wip-us.apache.org/repos/asf/hbase/blob/c64a1d19/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionServerCoprocessorHost.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionServerCoprocessorHost.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionServerCoprocessorHost.java
index f0dd0d7..7732827 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionServerCoprocessorHost.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/RegionServerCoprocessorHost.java
@@ -25,24 +25,26 @@ import java.util.List;
 import org.apache.commons.lang.ClassUtils;
 import org.apache.commons.logging.Log;
 import org.apache.commons.logging.LogFactory;
-import org.apache.hadoop.hbase.classification.InterfaceAudience;
-import org.apache.hadoop.hbase.classification.InterfaceStability;
 import org.apache.hadoop.conf.Configuration;
 import org.apache.hadoop.hbase.CellScanner;
 import org.apache.hadoop.hbase.Coprocessor;
 import org.apache.hadoop.hbase.CoprocessorEnvironment;
 import org.apache.hadoop.hbase.HBaseInterfaceAudience;
 import org.apache.hadoop.hbase.MetaMutationAnnotation;
+import org.apache.hadoop.hbase.classification.InterfaceAudience;
+import org.apache.hadoop.hbase.classification.InterfaceStability;
 import org.apache.hadoop.hbase.client.Mutation;
 import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
+import org.apache.hadoop.hbase.coprocessor.MetricsCoprocessor;
 import org.apache.hadoop.hbase.coprocessor.ObserverContext;
 import org.apache.hadoop.hbase.coprocessor.RegionServerCoprocessorEnvironment;
 import org.apache.hadoop.hbase.coprocessor.RegionServerObserver;
 import org.apache.hadoop.hbase.coprocessor.SingletonCoprocessorService;
 import org.apache.hadoop.hbase.ipc.RpcServer;
-import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.WALEntry;
+import org.apache.hadoop.hbase.metrics.MetricRegistry;
 import org.apache.hadoop.hbase.replication.ReplicationEndpoint;
 import org.apache.hadoop.hbase.security.User;
+import org.apache.hadoop.hbase.shaded.protobuf.generated.AdminProtos.WALEntry;
 
 @InterfaceAudience.LimitedPrivate(HBaseInterfaceAudience.COPROC)
 @InterfaceStability.Evolving
@@ -335,8 +337,8 @@ public class RegionServerCoprocessorHost extends
    */
   static class RegionServerEnvironment extends CoprocessorHost.Environment
       implements RegionServerCoprocessorEnvironment {
-
-    private RegionServerServices regionServerServices;
+    private final RegionServerServices regionServerServices;
+    private final MetricRegistry metricRegistry;
 
     @edu.umd.cs.findbugs.annotations.SuppressWarnings(value="BC_UNCONFIRMED_CAST",
         justification="Intentional; FB has trouble detecting isAssignableFrom")
@@ -353,12 +355,25 @@ public class RegionServerCoprocessorHost extends
           break;
         }
       }
+      this.metricRegistry =
+          MetricsCoprocessor.createRegistryForRSCoprocessor(implClass.getName());
     }
 
     @Override
     public RegionServerServices getRegionServerServices() {
       return regionServerServices;
     }
+
+    @Override
+    public MetricRegistry getMetricRegistryForRegionServer() {
+      return metricRegistry;
+    }
+
+    @Override
+    protected void shutdown() {
+      super.shutdown();
+      MetricsCoprocessor.removeRegistry(metricRegistry);
+    }
   }
 
   /**
@@ -367,6 +382,7 @@ public class RegionServerCoprocessorHost extends
    */
   static class EnvironmentPriorityComparator implements
       Comparator<CoprocessorEnvironment> {
+    @Override
     public int compare(final CoprocessorEnvironment env1,
         final CoprocessorEnvironment env2) {
       if (env1.getPriority() < env2.getPriority()) {

http://git-wip-us.apache.org/repos/asf/hbase/blob/c64a1d19/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/wal/WALCoprocessorHost.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/wal/WALCoprocessorHost.java b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/wal/WALCoprocessorHost.java
index 4d7e868..a531e83 100644
--- a/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/wal/WALCoprocessorHost.java
+++ b/hbase-server/src/main/java/org/apache/hadoop/hbase/regionserver/wal/WALCoprocessorHost.java
@@ -29,9 +29,11 @@ import org.apache.hadoop.hbase.Coprocessor;
 import org.apache.hadoop.hbase.HRegionInfo;
 import org.apache.hadoop.hbase.classification.InterfaceAudience;
 import org.apache.hadoop.hbase.coprocessor.CoprocessorHost;
+import org.apache.hadoop.hbase.coprocessor.MetricsCoprocessor;
 import org.apache.hadoop.hbase.coprocessor.ObserverContext;
 import org.apache.hadoop.hbase.coprocessor.WALCoprocessorEnvironment;
 import org.apache.hadoop.hbase.coprocessor.WALObserver;
+import org.apache.hadoop.hbase.metrics.MetricRegistry;
 import org.apache.hadoop.hbase.wal.WAL;
 import org.apache.hadoop.hbase.wal.WALKey;
 
@@ -51,6 +53,8 @@ public class WALCoprocessorHost
 
     private final WAL wal;
 
+    private final MetricRegistry metricRegistry;
+
     @Override
     public WAL getWAL() {
       return wal;
@@ -70,6 +74,18 @@ public class WALCoprocessorHost
         final WAL wal) {
       super(impl, priority, seq, conf);
       this.wal = wal;
+      this.metricRegistry = MetricsCoprocessor.createRegistryForWALCoprocessor(implClass.getName());
+    }
+
+    @Override
+    public MetricRegistry getMetricRegistryForRegionServer() {
+      return metricRegistry;
+    }
+
+    @Override
+    protected void shutdown() {
+      super.shutdown();
+      MetricsCoprocessor.removeRegistry(this.metricRegistry);
     }
   }
 

http://git-wip-us.apache.org/repos/asf/hbase/blob/c64a1d19/hbase-server/src/test/java/org/apache/hadoop/hbase/MiniHBaseCluster.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/MiniHBaseCluster.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/MiniHBaseCluster.java
index 190de4e..a8887d4 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/MiniHBaseCluster.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/MiniHBaseCluster.java
@@ -660,6 +660,13 @@ public class MiniHBaseCluster extends HBaseCluster {
     return hbaseCluster.getRegionServer(serverNumber);
   }
 
+  public HRegionServer getRegionServer(ServerName serverName) {
+    return hbaseCluster.getRegionServers().stream()
+        .map(t -> t.getRegionServer())
+        .filter(r -> r.getServerName().equals(serverName))
+        .findFirst().orElse(null);
+  }
+
   public List<HRegion> getRegions(byte[] tableName) {
     return getRegions(TableName.valueOf(tableName));
   }

http://git-wip-us.apache.org/repos/asf/hbase/blob/c64a1d19/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestCoprocessorMetrics.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestCoprocessorMetrics.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestCoprocessorMetrics.java
new file mode 100644
index 0000000..50e97ba
--- /dev/null
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/coprocessor/TestCoprocessorMetrics.java
@@ -0,0 +1,544 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hadoop.hbase.coprocessor;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertTrue;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Optional;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.hadoop.conf.Configuration;
+import org.apache.hadoop.hbase.Cell;
+import org.apache.hadoop.hbase.CoprocessorEnvironment;
+import org.apache.hadoop.hbase.HBaseTestingUtility;
+import org.apache.hadoop.hbase.HColumnDescriptor;
+import org.apache.hadoop.hbase.HRegionInfo;
+import org.apache.hadoop.hbase.HRegionLocation;
+import org.apache.hadoop.hbase.HTableDescriptor;
+import org.apache.hadoop.hbase.TableName;
+import org.apache.hadoop.hbase.client.Admin;
+import org.apache.hadoop.hbase.client.Connection;
+import org.apache.hadoop.hbase.client.ConnectionFactory;
+import org.apache.hadoop.hbase.client.Get;
+import org.apache.hadoop.hbase.client.Mutation;
+import org.apache.hadoop.hbase.client.Put;
+import org.apache.hadoop.hbase.client.RegionLocator;
+import org.apache.hadoop.hbase.client.Table;
+import org.apache.hadoop.hbase.ipc.CoprocessorRpcChannel;
+import org.apache.hadoop.hbase.metrics.Counter;
+import org.apache.hadoop.hbase.metrics.Metric;
+import org.apache.hadoop.hbase.metrics.MetricRegistries;
+import org.apache.hadoop.hbase.metrics.MetricRegistry;
+import org.apache.hadoop.hbase.metrics.MetricRegistryInfo;
+import org.apache.hadoop.hbase.metrics.Timer;
+import org.apache.hadoop.hbase.protobuf.ProtobufUtil;
+import org.apache.hadoop.hbase.protobuf.generated.ClientProtos;
+import org.apache.hadoop.hbase.protobuf.generated.MultiRowMutationProtos.MultiRowMutationService;
+import org.apache.hadoop.hbase.protobuf.generated.MultiRowMutationProtos.MutateRowsRequest;
+import org.apache.hadoop.hbase.protobuf.generated.MultiRowMutationProtos.MutateRowsResponse;
+import org.apache.hadoop.hbase.regionserver.HRegionServer;
+import org.apache.hadoop.hbase.regionserver.wal.WALEdit;
+import org.apache.hadoop.hbase.testclassification.CoprocessorTests;
+import org.apache.hadoop.hbase.testclassification.MediumTests;
+import org.apache.hadoop.hbase.util.Bytes;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import org.junit.experimental.categories.Category;
+
+import com.google.common.collect.Lists;
+import com.google.protobuf.RpcCallback;
+import com.google.protobuf.RpcController;
+import com.google.protobuf.ServiceException;
+
+/**
+ * Testing of coprocessor metrics end-to-end.
+ */
+@Category({CoprocessorTests.class, MediumTests.class})
+public class TestCoprocessorMetrics {
+
+  private static final Log LOG = LogFactory.getLog(TestCoprocessorMetrics.class);
+  private static final HBaseTestingUtility UTIL = new HBaseTestingUtility();
+
+  private static final byte[] foo = Bytes.toBytes("foo");
+  private static final byte[] bar = Bytes.toBytes("bar");
+  /**
+   * MasterObserver that has a Timer metric for create table operation.
+   */
+  public static class CustomMasterObserver extends BaseMasterObserver {
+    private Timer createTableTimer;
+    private long start = Long.MIN_VALUE;
+
+    @Override
+    public void preCreateTable(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                               HTableDescriptor desc, HRegionInfo[] regions) throws IOException {
+      super.preCreateTable(ctx, desc, regions);
+
+      // we rely on the fact that there is only 1 instance of our MasterObserver
+      this.start = System.currentTimeMillis();
+    }
+
+    @Override
+    public void postCreateTable(ObserverContext<MasterCoprocessorEnvironment> ctx,
+                                HTableDescriptor desc, HRegionInfo[] regions) throws IOException {
+      super.postCreateTable(ctx, desc, regions);
+      if (this.start > 0) {
+        long time = System.currentTimeMillis() - start;
+        LOG.info("Create table took: " + time);
+        createTableTimer.updateMillis(time);
+      }
+    }
+
+    @Override
+    public void start(CoprocessorEnvironment env) throws IOException {
+      super.start(env);
+      if (env instanceof MasterCoprocessorEnvironment) {
+        MetricRegistry registry =
+            ((MasterCoprocessorEnvironment) env).getMetricRegistryForMaster();
+
+        createTableTimer  = registry.timer("CreateTable");
+      }
+    }
+  }
+
+  /**
+   * RegionServerObserver that has a Counter for rollWAL requests.
+   */
+  public static class CustomRegionServerObserver extends BaseRegionServerObserver {
+    /** This is the Counter metric object to keep track of the current count across invocations */
+    private Counter rollWALCounter;
+    @Override
+    public void postRollWALWriterRequest(ObserverContext<RegionServerCoprocessorEnvironment> ctx)
+        throws IOException {
+      // Increment the Counter whenever the coprocessor is called
+      rollWALCounter.increment();
+      super.postRollWALWriterRequest(ctx);
+    }
+
+    @Override
+    public void start(CoprocessorEnvironment env) throws IOException {
+      super.start(env);
+      if (env instanceof RegionServerCoprocessorEnvironment) {
+        MetricRegistry registry =
+            ((RegionServerCoprocessorEnvironment) env).getMetricRegistryForRegionServer();
+
+        if (rollWALCounter == null) {
+          rollWALCounter = registry.counter("rollWALRequests");
+        }
+      }
+    }
+  }
+
+  /**
+   * WALObserver that has a Counter for walEdits written.
+   */
+  public static class CustomWALObserver extends BaseWALObserver {
+    private Counter walEditsCount;
+
+    @Override
+    public void postWALWrite(ObserverContext<? extends WALCoprocessorEnvironment> ctx,
+                             HRegionInfo info, org.apache.hadoop.hbase.wal.WALKey logKey,
+                             WALEdit logEdit) throws IOException {
+      super.postWALWrite(ctx, info, logKey, logEdit);
+      walEditsCount.increment();
+    }
+
+    @Override
+    public void start(CoprocessorEnvironment env) throws IOException {
+      super.start(env);
+      if (env instanceof WALCoprocessorEnvironment) {
+        MetricRegistry registry =
+            ((WALCoprocessorEnvironment) env).getMetricRegistryForRegionServer();
+
+        if (walEditsCount == null) {
+          walEditsCount = registry.counter("walEditsCount");
+        }
+      }
+    }
+  }
+
+  /**
+   * RegionObserver that has a Counter for preGet()
+   */
+  public static class CustomRegionObserver extends BaseRegionObserver {
+    private Counter preGetCounter;
+
+    @Override
+    public void preGetOp(ObserverContext<RegionCoprocessorEnvironment> e, Get get,
+                         List<Cell> results) throws IOException {
+      super.preGetOp(e, get, results);
+      preGetCounter.increment();
+    }
+
+    @Override
+    public void start(CoprocessorEnvironment env) throws IOException {
+      super.start(env);
+
+      if (env instanceof RegionCoprocessorEnvironment) {
+        MetricRegistry registry =
+            ((RegionCoprocessorEnvironment) env).getMetricRegistryForRegionServer();
+
+        if (preGetCounter == null) {
+          preGetCounter = registry.counter("preGetRequests");
+        }
+      }
+    }
+  }
+
+  public static class CustomRegionObserver2 extends CustomRegionObserver {
+  }
+
+  /**
+   * RegionEndpoint to test metrics from endpoint calls
+   */
+  public static class CustomRegionEndpoint extends MultiRowMutationEndpoint {
+
+    private Timer endpointExecution;
+
+    @Override
+    public void mutateRows(RpcController controller, MutateRowsRequest request,
+                           RpcCallback<MutateRowsResponse> done) {
+      long start = System.nanoTime();
+      super.mutateRows(controller, request, done);
+      endpointExecution.updateNanos(System.nanoTime() - start);
+    }
+
+    @Override
+    public void start(CoprocessorEnvironment env) throws IOException {
+      super.start(env);
+
+      if (env instanceof RegionCoprocessorEnvironment) {
+        MetricRegistry registry =
+            ((RegionCoprocessorEnvironment) env).getMetricRegistryForRegionServer();
+
+        if (endpointExecution == null) {
+          endpointExecution = registry.timer("EndpointExecution");
+        }
+      }
+    }
+  }
+
+  @BeforeClass
+  public static void setupBeforeClass() throws Exception {
+    Configuration conf = UTIL.getConfiguration();
+    // inject master, regionserver and WAL coprocessors
+    conf.set(CoprocessorHost.MASTER_COPROCESSOR_CONF_KEY,
+        CustomMasterObserver.class.getName());
+    conf.set(CoprocessorHost.REGIONSERVER_COPROCESSOR_CONF_KEY,
+        CustomRegionServerObserver.class.getName());
+    conf.set(CoprocessorHost.WAL_COPROCESSOR_CONF_KEY,
+        CustomWALObserver.class.getName());
+    conf.setBoolean(CoprocessorHost.ABORT_ON_ERROR_KEY, true);
+    UTIL.startMiniCluster();
+  }
+
+  @AfterClass
+  public static void teardownAfterClass() throws Exception {
+    UTIL.shutdownMiniCluster();
+  }
+
+  @Before
+  public void setup() throws IOException {
+    try (Connection connection = ConnectionFactory.createConnection(UTIL.getConfiguration());
+         Admin admin = connection.getAdmin()) {
+      for (HTableDescriptor htd : admin.listTables()) {
+        UTIL.deleteTable(htd.getTableName());
+      }
+    }
+  }
+
+  @Test
+  public void testMasterObserver() throws IOException {
+    // Find out the MetricRegistry used by the CP using the global registries
+    MetricRegistryInfo info = MetricsCoprocessor.createRegistryInfoForMasterCoprocessor(
+        CustomMasterObserver.class.getName());
+    Optional<MetricRegistry> registry =  MetricRegistries.global().get(info);
+    assertTrue(registry.isPresent());
+
+    Optional<Metric> metric = registry.get().get("CreateTable");
+    assertTrue(metric.isPresent());
+
+    try (Connection connection = ConnectionFactory.createConnection(UTIL.getConfiguration());
+         Admin admin = connection.getAdmin()) {
+
+      Timer createTableTimer = (Timer)metric.get();
+      long prevCount = createTableTimer.getHistogram().getCount();
+      LOG.info("Creating table");
+      admin.createTable(
+          new HTableDescriptor("testMasterObserver")
+              .addFamily(new HColumnDescriptor("foo")));
+
+      assertEquals(1, createTableTimer.getHistogram().getCount() - prevCount);
+    }
+  }
+
+  @Test
+  public void testRegionServerObserver() throws IOException {
+    try (Connection connection = ConnectionFactory.createConnection(UTIL.getConfiguration());
+         Admin admin = connection.getAdmin()) {
+      LOG.info("Rolling WALs");
+      admin.rollWALWriter(UTIL.getMiniHBaseCluster().getServerHoldingMeta());
+    }
+
+    // Find out the MetricRegistry used by the CP using the global registries
+    MetricRegistryInfo info = MetricsCoprocessor.createRegistryInfoForRSCoprocessor(
+        CustomRegionServerObserver.class.getName());
+
+    Optional<MetricRegistry> registry =  MetricRegistries.global().get(info);
+    assertTrue(registry.isPresent());
+
+    Optional<Metric> metric = registry.get().get("rollWALRequests");
+    assertTrue(metric.isPresent());
+
+    Counter rollWalRequests = (Counter)metric.get();
+    assertEquals(1, rollWalRequests.getCount());
+  }
+
+  @Test
+  public void testWALObserver() throws IOException {
+    // Find out the MetricRegistry used by the CP using the global registries
+    MetricRegistryInfo info = MetricsCoprocessor.createRegistryInfoForWALCoprocessor(
+        CustomWALObserver.class.getName());
+
+    Optional<MetricRegistry> registry =  MetricRegistries.global().get(info);
+    assertTrue(registry.isPresent());
+
+    Optional<Metric> metric = registry.get().get("walEditsCount");
+    assertTrue(metric.isPresent());
+
+    try (Connection connection = ConnectionFactory.createConnection(UTIL.getConfiguration());
+         Admin admin = connection.getAdmin()) {
+      admin.createTable(
+          new HTableDescriptor("testWALObserver")
+              .addFamily(new HColumnDescriptor("foo")));
+
+      Counter rollWalRequests = (Counter)metric.get();
+      long prevCount = rollWalRequests.getCount();
+      assertTrue(prevCount > 0);
+
+      try (Table table = connection.getTable(TableName.valueOf("testWALObserver"))) {
+        table.put(new Put(foo).addColumn(foo, foo, foo));
+      }
+
+      assertEquals(1, rollWalRequests.getCount() - prevCount);
+    }
+  }
+
+  /**
+   * Helper for below tests
+   */
+  private void assertPreGetRequestsCounter(Class<?> coprocClass) {
+    // Find out the MetricRegistry used by the CP using the global registries
+    MetricRegistryInfo info = MetricsCoprocessor.createRegistryInfoForRegionCoprocessor(
+        coprocClass.getName());
+
+    Optional<MetricRegistry> registry =  MetricRegistries.global().get(info);
+    assertTrue(registry.isPresent());
+
+    Optional<Metric> metric = registry.get().get("preGetRequests");
+    assertTrue(metric.isPresent());
+
+    Counter preGetRequests = (Counter)metric.get();
+    assertEquals(2, preGetRequests.getCount());
+  }
+
+  @Test
+  public void testRegionObserverSingleRegion() throws IOException {
+    TableName tableName = TableName.valueOf("testRegionObserverSingleRegion");
+    try (Connection connection = ConnectionFactory.createConnection(UTIL.getConfiguration());
+         Admin admin = connection.getAdmin()) {
+      admin.createTable(
+          new HTableDescriptor(tableName)
+              .addFamily(new HColumnDescriptor(foo))
+              // add the coprocessor for the region
+              .addCoprocessor(CustomRegionObserver.class.getName()));
+      try (Table table = connection.getTable(tableName)) {
+        table.get(new Get(foo));
+        table.get(new Get(foo)); // 2 gets
+      }
+    }
+
+    assertPreGetRequestsCounter(CustomRegionObserver.class);
+  }
+
+  @Test
+  public void testRegionObserverMultiRegion() throws IOException {
+    TableName tableName = TableName.valueOf("testRegionObserverMultiRegion");
+    try (Connection connection = ConnectionFactory.createConnection(UTIL.getConfiguration());
+         Admin admin = connection.getAdmin()) {
+      admin.createTable(
+          new HTableDescriptor(tableName)
+              .addFamily(new HColumnDescriptor(foo))
+              // add the coprocessor for the region
+              .addCoprocessor(CustomRegionObserver.class.getName())
+          , new byte[][]{foo}); // create with 2 regions
+      try (Table table = connection.getTable(tableName);
+           RegionLocator locator = connection.getRegionLocator(tableName)) {
+        table.get(new Get(bar));
+        table.get(new Get(foo)); // 2 gets to 2 separate regions
+        assertEquals(2, locator.getAllRegionLocations().size());
+        assertNotEquals(locator.getRegionLocation(bar).getRegionInfo(),
+            locator.getRegionLocation(foo).getRegionInfo());
+      }
+    }
+
+    assertPreGetRequestsCounter(CustomRegionObserver.class);
+  }
+
+  @Test
+  public void testRegionObserverMultiTable() throws IOException {
+    TableName tableName1 = TableName.valueOf("testRegionObserverMultiTable1");
+    TableName tableName2 = TableName.valueOf("testRegionObserverMultiTable2");
+    try (Connection connection = ConnectionFactory.createConnection(UTIL.getConfiguration());
+         Admin admin = connection.getAdmin()) {
+      admin.createTable(
+          new HTableDescriptor(tableName1)
+              .addFamily(new HColumnDescriptor(foo))
+              // add the coprocessor for the region
+              .addCoprocessor(CustomRegionObserver.class.getName()));
+      admin.createTable(
+          new HTableDescriptor(tableName2)
+              .addFamily(new HColumnDescriptor(foo))
+              // add the coprocessor for the region
+              .addCoprocessor(CustomRegionObserver.class.getName()));
+      try (Table table1 = connection.getTable(tableName1);
+           Table table2 = connection.getTable(tableName2);) {
+        table1.get(new Get(bar));
+        table2.get(new Get(foo)); // 2 gets to 2 separate tables
+      }
+    }
+    assertPreGetRequestsCounter(CustomRegionObserver.class);
+  }
+
+  @Test
+  public void testRegionObserverMultiCoprocessor() throws IOException {
+    TableName tableName = TableName.valueOf("testRegionObserverMultiCoprocessor");
+    try (Connection connection = ConnectionFactory.createConnection(UTIL.getConfiguration());
+         Admin admin = connection.getAdmin()) {
+      admin.createTable(
+          new HTableDescriptor(tableName)
+              .addFamily(new HColumnDescriptor(foo))
+              // add the coprocessor for the region. We add two different coprocessors
+              .addCoprocessor(CustomRegionObserver.class.getName())
+              .addCoprocessor(CustomRegionObserver2.class.getName()));
+      try (Table table = connection.getTable(tableName)) {
+        table.get(new Get(foo));
+        table.get(new Get(foo)); // 2 gets
+      }
+    }
+
+    // we will have two counters coming from two coprocs, in two different MetricRegistries
+    assertPreGetRequestsCounter(CustomRegionObserver.class);
+    assertPreGetRequestsCounter(CustomRegionObserver2.class);
+  }
+
+  @Test
+  public void testRegionObserverAfterRegionClosed() throws IOException {
+    TableName tableName = TableName.valueOf("testRegionObserverAfterRegionClosed");
+    try (Connection connection = ConnectionFactory.createConnection(UTIL.getConfiguration());
+         Admin admin = connection.getAdmin()) {
+      admin.createTable(
+          new HTableDescriptor(tableName)
+              .addFamily(new HColumnDescriptor(foo))
+              // add the coprocessor for the region
+              .addCoprocessor(CustomRegionObserver.class.getName())
+          , new byte[][]{foo}); // create with 2 regions
+      try (Table table = connection.getTable(tableName)) {
+        table.get(new Get(foo));
+        table.get(new Get(foo)); // 2 gets
+      }
+
+      assertPreGetRequestsCounter(CustomRegionObserver.class);
+
+      // close one of the regions
+      try (RegionLocator locator = connection.getRegionLocator(tableName)) {
+        HRegionLocation loc = locator.getRegionLocation(foo);
+        admin.closeRegion(loc.getServerName(), loc.getRegionInfo());
+
+        HRegionServer server = UTIL.getMiniHBaseCluster().getRegionServer(loc.getServerName());
+        UTIL.waitFor(30000,
+            () -> server.getOnlineRegion(loc.getRegionInfo().getRegionName()) == null);
+        assertNull(server.getOnlineRegion(loc.getRegionInfo().getRegionName()));
+      }
+
+      // with only 1 region remaining, we should still be able to find the Counter
+      assertPreGetRequestsCounter(CustomRegionObserver.class);
+
+      // close the table
+      admin.disableTable(tableName);
+
+      MetricRegistryInfo info = MetricsCoprocessor.createRegistryInfoForRegionCoprocessor(
+          CustomRegionObserver.class.getName());
+
+      // ensure that MetricRegistry is deleted
+      Optional<MetricRegistry> registry =  MetricRegistries.global().get(info);
+      assertFalse(registry.isPresent());
+    }
+  }
+
+  @Test
+  public void testRegionObserverEndpoint() throws IOException, ServiceException {
+    TableName tableName = TableName.valueOf("testRegionObserverEndpoint");
+    try (Connection connection = ConnectionFactory.createConnection(UTIL.getConfiguration());
+         Admin admin = connection.getAdmin()) {
+      admin.createTable(
+          new HTableDescriptor(tableName)
+              .addFamily(new HColumnDescriptor(foo))
+              // add the coprocessor for the region
+              .addCoprocessor(CustomRegionEndpoint.class.getName()));
+
+      try (Table table = connection.getTable(tableName)) {
+        List<Mutation> mutations = Lists.newArrayList(new Put(foo), new Put(bar));
+        MutateRowsRequest.Builder mrmBuilder = MutateRowsRequest.newBuilder();
+
+        for (Mutation mutation : mutations) {
+          mrmBuilder.addMutationRequest(ProtobufUtil.toMutation(
+              ClientProtos.MutationProto.MutationType.PUT, mutation));
+        }
+
+        CoprocessorRpcChannel channel = table.coprocessorService(bar);
+        MultiRowMutationService.BlockingInterface service =
+            MultiRowMutationService.newBlockingStub(channel);
+        MutateRowsRequest mrm = mrmBuilder.build();
+        service.mutateRows(null, mrm);
+      }
+    }
+
+    // Find out the MetricRegistry used by the CP using the global registries
+    MetricRegistryInfo info = MetricsCoprocessor.createRegistryInfoForRegionCoprocessor(
+        CustomRegionEndpoint.class.getName());
+
+    Optional<MetricRegistry> registry =  MetricRegistries.global().get(info);
+    assertTrue(registry.isPresent());
+
+    Optional<Metric> metric = registry.get().get("EndpointExecution");
+    assertTrue(metric.isPresent());
+
+    Timer endpointExecutions = (Timer)metric.get();
+    assertEquals(1, endpointExecutions.getHistogram().getCount());
+  }
+}

http://git-wip-us.apache.org/repos/asf/hbase/blob/c64a1d19/hbase-server/src/test/java/org/apache/hadoop/hbase/security/token/TestTokenAuthentication.java
----------------------------------------------------------------------
diff --git a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/token/TestTokenAuthentication.java b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/token/TestTokenAuthentication.java
index c8fe299..5b46af5 100644
--- a/hbase-server/src/test/java/org/apache/hadoop/hbase/security/token/TestTokenAuthentication.java
+++ b/hbase-server/src/test/java/org/apache/hadoop/hbase/security/token/TestTokenAuthentication.java
@@ -54,6 +54,7 @@ import org.apache.hadoop.hbase.ipc.RpcServer.BlockingServiceAndInterface;
 import org.apache.hadoop.hbase.ipc.RpcServerFactory;
 import org.apache.hadoop.hbase.ipc.RpcServerInterface;
 import org.apache.hadoop.hbase.ipc.ServerRpcController;
+import org.apache.hadoop.hbase.metrics.MetricRegistry;
 import org.apache.hadoop.hbase.protobuf.generated.AuthenticationProtos;
 import org.apache.hadoop.hbase.regionserver.HRegion;
 import org.apache.hadoop.hbase.regionserver.RegionServerServices;
@@ -260,6 +261,11 @@ public class TestTokenAuthentication {
         public ConcurrentMap<String, Object> getSharedData() { return null; }
 
         @Override
+        public MetricRegistry getMetricRegistryForRegionServer() {
+          return null;
+        }
+
+        @Override
         public int getVersion() { return 0; }
 
         @Override

http://git-wip-us.apache.org/repos/asf/hbase/blob/c64a1d19/pom.xml
----------------------------------------------------------------------
diff --git a/pom.xml b/pom.xml
index 55c5879..6d837d6 100644
--- a/pom.xml
+++ b/pom.xml
@@ -84,6 +84,8 @@
     <module>hbase-shaded</module>
     <module>hbase-spark</module>
     <module>hbase-archetypes</module>
+    <module>hbase-metrics-api</module>
+    <module>hbase-metrics</module>
   </modules>
   <!--Add apache snapshots in case we want to use unreleased versions of plugins:
       e.g. surefire 2.18-SNAPSHOT-->
@@ -1499,6 +1501,30 @@
         <groupId>org.apache.hbase</groupId>
         <version>${project.version}</version>
       </dependency>
+      <dependency>
+        <artifactId>hbase-metrics-api</artifactId>
+        <groupId>org.apache.hbase</groupId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <artifactId>hbase-metrics-api</artifactId>
+        <groupId>org.apache.hbase</groupId>
+        <version>${project.version}</version>
+        <type>test-jar</type>
+        <scope>test</scope>
+      </dependency>
+      <dependency>
+        <artifactId>hbase-metrics</artifactId>
+        <groupId>org.apache.hbase</groupId>
+        <version>${project.version}</version>
+      </dependency>
+      <dependency>
+        <artifactId>hbase-metrics</artifactId>
+        <groupId>org.apache.hbase</groupId>
+        <version>${project.version}</version>
+        <type>test-jar</type>
+        <scope>test</scope>
+      </dependency>
       <!-- General dependencies -->
       <dependency>
         <groupId>com.github.stephenc.findbugs</groupId>


Mime
View raw message