diff --git a/build.xml b/build.xml
index d8d4bb764b..f72f54f2b0 100644
--- a/build.xml
+++ b/build.xml
@@ -592,7 +592,7 @@
-
+
diff --git a/test/distributed/org/apache/cassandra/distributed/impl/AbstractCluster.java b/test/distributed/org/apache/cassandra/distributed/impl/AbstractCluster.java
index 1ae594fbe6..0eea077950 100644
--- a/test/distributed/org/apache/cassandra/distributed/impl/AbstractCluster.java
+++ b/test/distributed/org/apache/cassandra/distributed/impl/AbstractCluster.java
@@ -67,6 +67,7 @@ import org.apache.cassandra.distributed.api.NodeToolResult;
import org.apache.cassandra.distributed.api.TokenSupplier;
import org.apache.cassandra.distributed.shared.InstanceClassLoader;
import org.apache.cassandra.distributed.shared.MessageFilters;
+import org.apache.cassandra.distributed.shared.Metrics;
import org.apache.cassandra.distributed.shared.NetworkTopology;
import org.apache.cassandra.distributed.shared.Shared;
import org.apache.cassandra.distributed.shared.ShutdownException;
@@ -256,6 +257,14 @@ public abstract class AbstractCluster implements ICluster new InstanceMetrics(CassandraMetricsRegistry.Metrics));
+ }
+
public NodeToolResult nodetoolResult(boolean withNotifications, String... commandAndArgs)
{
return sync(() -> {
diff --git a/test/distributed/org/apache/cassandra/distributed/impl/InstanceMetrics.java b/test/distributed/org/apache/cassandra/distributed/impl/InstanceMetrics.java
new file mode 100644
index 0000000000..3bd58940fe
--- /dev/null
+++ b/test/distributed/org/apache/cassandra/distributed/impl/InstanceMetrics.java
@@ -0,0 +1,183 @@
+/*
+ * 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.cassandra.distributed.impl;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Predicate;
+
+import com.codahale.metrics.Counter;
+import com.codahale.metrics.Gauge;
+import com.codahale.metrics.Histogram;
+import com.codahale.metrics.Meter;
+import com.codahale.metrics.Snapshot;
+import com.codahale.metrics.Timer;
+import org.apache.cassandra.distributed.shared.Metrics;
+import org.apache.cassandra.metrics.CassandraMetricsRegistry;
+
+/**
+ * Pulls metrics out of in-JVM dtest cluster instance.
+ */
+class InstanceMetrics implements Metrics
+{
+ private final CassandraMetricsRegistry metricsRegistry;
+
+ InstanceMetrics(CassandraMetricsRegistry metricsRegistry)
+ {
+ this.metricsRegistry = metricsRegistry;
+ }
+
+ public List getNames()
+ {
+ return new ArrayList<>(metricsRegistry.getNames());
+ }
+
+ public long getCounter(String name)
+ {
+ return metricsRegistry.getCounters().get(name).getCount();
+ }
+
+ public Map getCounters(Predicate filter)
+ {
+ Map values = new HashMap<>();
+ for (Map.Entry e : metricsRegistry.getCounters().entrySet())
+ {
+ if (filter.test(e.getKey()))
+ values.put(e.getKey(), e.getValue().getCount());
+ }
+ return values;
+ }
+
+ public double getHistogram(String name, MetricValue value)
+ {
+ Histogram histogram = metricsRegistry.getHistograms().get(name);
+ if (value == MetricValue.COUNT)
+ return histogram.getCount();
+
+ return getValue(histogram.getSnapshot(), value);
+ }
+
+ public Map getHistograms(Predicate filter, MetricValue value)
+ {
+ Map values = new HashMap<>();
+ for (Map.Entry e : metricsRegistry.getHistograms().entrySet())
+ {
+ if (filter.test(e.getKey()))
+ values.put(e.getKey(), getValue(e.getValue().getSnapshot(), value));
+ }
+ return values;
+ }
+
+ public Object getGauge(String name)
+ {
+ return metricsRegistry.getGauges().get(name).getValue();
+ }
+
+ public Map getGauges(Predicate filter)
+ {
+ Map values = new HashMap<>();
+ for (Map.Entry e : metricsRegistry.getGauges().entrySet())
+ {
+ if (filter.test(e.getKey()))
+ values.put(e.getKey(), e.getValue().getValue());
+ }
+ return values;
+ }
+
+ public double getMeter(String name, Rate value)
+ {
+ return getRate(metricsRegistry.getMeters().get(name), value);
+ }
+
+ public Map getMeters(Predicate filter, Rate rate)
+ {
+ Map values = new HashMap<>();
+ for (Map.Entry e : metricsRegistry.getMeters().entrySet())
+ {
+ if (filter.test(e.getKey()))
+ values.put(e.getKey(), getRate(e.getValue(), rate));
+ }
+ return values;
+ }
+
+ public double getTimer(String name, MetricValue value)
+ {
+ return getValue(metricsRegistry.getTimers().get(name).getSnapshot(), value);
+ }
+
+ public Map getTimers(Predicate filter, MetricValue value)
+ {
+ Map values = new HashMap<>();
+ for (Map.Entry e : metricsRegistry.getTimers().entrySet())
+ {
+ if (filter.test(e.getKey()))
+ values.put(e.getKey(), getValue(e.getValue().getSnapshot(), value));
+ }
+
+ return values;
+ }
+
+ static double getValue(Snapshot snapshot, MetricValue value)
+ {
+ switch (value)
+ {
+ case MEDIAN:
+ return snapshot.getMedian();
+ case P75:
+ return snapshot.get75thPercentile();
+ case P95:
+ return snapshot.get95thPercentile();
+ case P98:
+ return snapshot.get98thPercentile();
+ case P99:
+ return snapshot.get99thPercentile();
+ case P999:
+ return snapshot.get999thPercentile();
+ case MAX:
+ return snapshot.getMax();
+ case MEAN:
+ return snapshot.getMean();
+ case MIN:
+ return snapshot.getMin();
+ case STDDEV:
+ return snapshot.getStdDev();
+ default:
+ throw new RuntimeException("Shouldn't happen");
+ }
+ }
+
+ static double getRate(Meter meter, Rate rate)
+ {
+ switch (rate)
+ {
+ case RATE15_MIN:
+ return meter.getFifteenMinuteRate();
+ case RATE5_MIN:
+ return meter.getFiveMinuteRate();
+ case RATE1_MIN:
+ return meter.getOneMinuteRate();
+ case RATE_MEAN:
+ return meter.getMeanRate();
+ default:
+ throw new RuntimeException("Shouldn't happen");
+ }
+ }
+}
diff --git a/test/distributed/org/apache/cassandra/distributed/test/MetricsTest.java b/test/distributed/org/apache/cassandra/distributed/test/MetricsTest.java
new file mode 100644
index 0000000000..c9e110ad14
--- /dev/null
+++ b/test/distributed/org/apache/cassandra/distributed/test/MetricsTest.java
@@ -0,0 +1,48 @@
+/*
+ * 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.cassandra.distributed.test;
+
+import com.google.common.collect.ImmutableMap;
+import org.junit.Assert;
+import org.junit.Test;
+
+import org.apache.cassandra.distributed.api.ICluster;
+import org.apache.cassandra.distributed.api.IInvokableInstance;
+import org.apache.cassandra.metrics.CassandraMetricsRegistry;
+
+public class MetricsTest extends TestBaseImpl
+{
+ @Test
+ public void testMetrics() throws Throwable
+ {
+ try (ICluster cluster = init(builder().withNodes(1)
+ .start()))
+ {
+ cluster.get(1).runOnInstance(() -> {
+ CassandraMetricsRegistry.Metrics.counter("test_counter").inc(100);
+ CassandraMetricsRegistry.Metrics.counter("test_counter_2").inc(101);
+ });
+
+ Assert.assertEquals(100, cluster.get(1).metrics().getCounter("test_counter"));
+ Assert.assertEquals(ImmutableMap.of("test_counter", 100L,
+ "test_counter_2", 101L),
+ cluster.get(1).metrics().getCounters(s -> s.startsWith("test_counter")));
+ }
+ }
+}