Skip to content

Commit 7ebabf3

Browse files
committed
Merge branch 'master' into metric-name
Conflicts: metrics-core/src/main/java/io/dropwizard/metrics/MetricRegistry.java metrics-core/src/main/java/io/dropwizard/metrics/Slf4jReporter.java metrics-core/src/test/java/io/dropwizard/metrics/JmxAttributeGaugeTest.java
2 parents fb76ba2 + e847f50 commit 7ebabf3

File tree

22 files changed

+1359
-50
lines changed

22 files changed

+1359
-50
lines changed

metrics-core/src/main/java/io/dropwizard/metrics/JmxAttributeGauge.java

+12-1
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import javax.management.ObjectName;
1010

1111
import java.lang.management.ManagementFactory;
12+
import java.util.Set;
1213

1314
/**
1415
* A {@link Gauge} implementation which queries an {@link MBeanServerConnection} for an attribute of an object.
@@ -44,11 +45,21 @@ public JmxAttributeGauge(MBeanServerConnection mBeanServerConn, ObjectName objec
4445
@Override
4546
public Object getValue() {
4647
try {
47-
return mBeanServerConn.getAttribute(objectName, attributeName);
48+
return mBeanServerConn.getAttribute(getObjectName(), attributeName);
4849
} catch (IOException e) {
4950
return null;
5051
} catch (JMException e) {
5152
return null;
5253
}
5354
}
55+
56+
private ObjectName getObjectName() throws IOException {
57+
if (objectName.isPattern()) {
58+
Set<ObjectName> foundNames = mBeanServerConn.queryNames(objectName, null);
59+
if (foundNames.size() == 1) {
60+
return foundNames.iterator().next();
61+
}
62+
}
63+
return objectName;
64+
}
5465
}

metrics-core/src/main/java/io/dropwizard/metrics/MetricRegistry.java

+7-9
Original file line numberDiff line numberDiff line change
@@ -66,19 +66,17 @@ public static MetricName name(String name, String... names) {
6666
* Creates a new {@link MetricRegistry}.
6767
*/
6868
public MetricRegistry() {
69-
this.metrics = buildMap();
70-
this.listeners = new CopyOnWriteArrayList<MetricRegistryListener>();
69+
this(new ConcurrentHashMap<MetricName, Metric>());
7170
}
7271

7372
/**
74-
* Creates a new {@link ConcurrentMap} implementation for use inside the registry. Override this
75-
* to create a {@link MetricRegistry} with space- or time-bounded metric lifecycles, for
76-
* example.
77-
*
78-
* @return a new {@link ConcurrentMap}
73+
* Creates a {@link MetricRegistry} with a custom {@link ConcurrentMap} implementation for use
74+
* inside the registry. Call as the super-constructor to create a {@link MetricRegistry} with
75+
* space- or time-bounded metric lifecycles, for example.
7976
*/
80-
protected ConcurrentMap<MetricName, Metric> buildMap() {
81-
return new ConcurrentHashMap<MetricName, Metric>();
77+
protected MetricRegistry(ConcurrentMap<MetricName, Metric> metricsMap) {
78+
this.metrics = metricsMap;
79+
this.listeners = new CopyOnWriteArrayList<MetricRegistryListener>();
8280
}
8381

8482
/**

metrics-core/src/main/java/io/dropwizard/metrics/SharedMetricRegistries.java

+23
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ public class SharedMetricRegistries {
1313
private static final ConcurrentMap<String, MetricRegistry> REGISTRIES =
1414
new ConcurrentHashMap<String, MetricRegistry>();
1515

16+
private static volatile String defaultRegistryName = null;
17+
1618
private SharedMetricRegistries() { /* singleton */ }
1719

1820
public static void clear() {
@@ -43,4 +45,25 @@ public static MetricRegistry getOrCreate(String name) {
4345
}
4446
return existing;
4547
}
48+
49+
public synchronized static MetricRegistry setDefault(String name) {
50+
final MetricRegistry registry = getOrCreate(name);
51+
return setDefault(name, registry);
52+
}
53+
54+
public static MetricRegistry setDefault(String name, MetricRegistry metricRegistry) {
55+
if (defaultRegistryName == null) {
56+
defaultRegistryName = name;
57+
add(name, metricRegistry);
58+
return metricRegistry;
59+
}
60+
throw new IllegalStateException("Default metric registry name is already set.");
61+
}
62+
63+
public static MetricRegistry getDefault() {
64+
if (defaultRegistryName != null) {
65+
return getOrCreate(defaultRegistryName);
66+
}
67+
throw new IllegalStateException("Default registry name has not been set.");
68+
}
4669
}

metrics-core/src/main/java/io/dropwizard/metrics/Slf4jReporter.java

+8-5
Original file line numberDiff line numberDiff line change
@@ -221,9 +221,10 @@ public void report(SortedMap<MetricName, Gauge> gauges,
221221
private void logTimer(MetricName name, Timer timer) {
222222
final Snapshot snapshot = timer.getSnapshot();
223223
loggerProxy.log(marker,
224-
"type=TIMER, name={}, count={}, min={}, max={}, mean={}, stddev={}, median={}, " +
224+
"type={}, name={}, count={}, min={}, max={}, mean={}, stddev={}, median={}, " +
225225
"p75={}, p95={}, p98={}, p99={}, p999={}, mean_rate={}, m1={}, m5={}, " +
226226
"m15={}, rate_unit={}, duration_unit={}",
227+
"TIMER",
227228
prefix(name),
228229
timer.getCount(),
229230
convertDuration(snapshot.getMin()),
@@ -246,7 +247,8 @@ private void logTimer(MetricName name, Timer timer) {
246247

247248
private void logMeter(MetricName name, Meter meter) {
248249
loggerProxy.log(marker,
249-
"type=METER, name={}, count={}, mean_rate={}, m1={}, m5={}, m15={}, rate_unit={}",
250+
"type={}, name={}, count={}, mean_rate={}, m1={}, m5={}, m15={}, rate_unit={}",
251+
"METER",
250252
prefix(name),
251253
meter.getCount(),
252254
convertRate(meter.getMeanRate()),
@@ -259,8 +261,9 @@ private void logMeter(MetricName name, Meter meter) {
259261
private void logHistogram(MetricName name, Histogram histogram) {
260262
final Snapshot snapshot = histogram.getSnapshot();
261263
loggerProxy.log(marker,
262-
"type=HISTOGRAM, name={}, count={}, min={}, max={}, mean={}, stddev={}, " +
264+
"type={}, name={}, count={}, min={}, max={}, mean={}, stddev={}, " +
263265
"median={}, p75={}, p95={}, p98={}, p99={}, p999={}",
266+
"HISTOGRAM",
264267
prefix(name),
265268
histogram.getCount(),
266269
snapshot.getMin(),
@@ -276,11 +279,11 @@ private void logHistogram(MetricName name, Histogram histogram) {
276279
}
277280

278281
private void logCounter(MetricName name, Counter counter) {
279-
loggerProxy.log(marker, "type=COUNTER, name={}, count={}", prefix(name), counter.getCount());
282+
loggerProxy.log(marker, "type={}, name={}, count={}", "COUNTER", prefix(name), counter.getCount());
280283
}
281284

282285
private void logGauge(MetricName name, Gauge gauge) {
283-
loggerProxy.log(marker, "type=GAUGE, name={}, value={}", prefix(name), gauge.getValue());
286+
loggerProxy.log(marker, "type={}, name={}, value={}", "GAUGE", prefix(name), gauge.getValue());
284287
}
285288

286289
@Override

metrics-core/src/main/java/io/dropwizard/metrics/Timer.java

+16-1
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ public void update(long duration, TimeUnit unit) {
9797
}
9898

9999
/**
100-
* Times and records the duration of event.
100+
* Times and records the duration of an event.
101101
*
102102
* @param event a {@link Callable} whose {@link Callable#call()} method implements a process
103103
* whose duration should be timed
@@ -114,6 +114,21 @@ public <T> T time(Callable<T> event) throws Exception {
114114
}
115115
}
116116

117+
/**
118+
* Times and records the duration of an event.
119+
*
120+
* @param event a {@link Runnable} whose {@link Runnable#run()} method implements a process
121+
* whose duration should be timed
122+
*/
123+
public void time(Runnable event) {
124+
long startTime = this.clock.getTick();
125+
try {
126+
event.run();
127+
} finally {
128+
update(this.clock.getTick() - startTime);
129+
}
130+
}
131+
117132
/**
118133
* Returns a new {@link Context}.
119134
*
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,99 @@
11
package io.dropwizard.metrics;
22

3-
import org.junit.Test;
3+
import static org.assertj.core.api.Assertions.assertThat;
44

5-
import io.dropwizard.metrics.JmxAttributeGauge;
5+
import java.lang.management.ManagementFactory;
6+
import java.util.ArrayList;
7+
import java.util.List;
68

7-
import javax.management.AttributeNotFoundException;
9+
import javax.management.JMException;
810
import javax.management.MBeanServer;
11+
import javax.management.ObjectInstance;
912
import javax.management.ObjectName;
1013

11-
import static org.assertj.core.api.Assertions.assertThat;
12-
import static org.mockito.Mockito.mock;
13-
import static org.mockito.Mockito.when;
14+
import org.junit.AfterClass;
15+
import org.junit.BeforeClass;
16+
import org.junit.Test;
1417

1518
public class JmxAttributeGaugeTest {
16-
private final MBeanServer mBeanServer = mock(MBeanServer.class);
17-
private final ObjectName objectName = mock(ObjectName.class);
18-
private final JmxAttributeGauge gauge = new JmxAttributeGauge(mBeanServer, objectName, "attr");
19-
private final Object value = mock(Object.class);
19+
20+
private static MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
21+
22+
private static List<ObjectName> registeredMBeans = new ArrayList<ObjectName>();
23+
24+
public interface JmxTestMBean {
25+
Long getValue();
26+
}
27+
28+
private static class JmxTest implements JmxTestMBean {
29+
@Override
30+
public Long getValue() {
31+
return Long.MAX_VALUE;
32+
}
33+
}
34+
35+
@BeforeClass
36+
public static void setUp() throws Exception {
37+
registerMBean(new ObjectName("JmxAttributeGaugeTest:type=test,name=test1"));
38+
registerMBean(new ObjectName("JmxAttributeGaugeTest:type=test,name=test2"));
39+
}
40+
41+
@AfterClass
42+
public static void tearDown() {
43+
for (ObjectName objectName : registeredMBeans) {
44+
try {
45+
mBeanServer.unregisterMBean(objectName);
46+
} catch (Exception e) {
47+
// ignore
48+
}
49+
}
50+
}
2051

2152
@Test
22-
public void returnsAJmxAttribute() throws Exception {
23-
when(mBeanServer.getAttribute(objectName, "attr")).thenReturn(value);
53+
public void returnsJmxAttribute() throws Exception {
54+
ObjectName objectName = new ObjectName("java.lang:type=ClassLoading");
55+
JmxAttributeGauge gauge = new JmxAttributeGauge(mBeanServer, objectName, "LoadedClassCount");
2456

25-
assertThat(gauge.getValue())
26-
.isEqualTo(value);
57+
assertThat(gauge.getValue()).isInstanceOf(Integer.class);
58+
assertThat((Integer) gauge.getValue()).isGreaterThan(0);
2759
}
2860

2961
@Test
30-
public void returnsNullIfThereIsAnException() throws Exception {
31-
when(mBeanServer.getAttribute(objectName, "attr")).thenThrow(new AttributeNotFoundException());
62+
public void returnsNullIfAttributeDoesNotExist() throws Exception {
63+
ObjectName objectName = new ObjectName("java.lang:type=ClassLoading");
64+
JmxAttributeGauge gauge = new JmxAttributeGauge(mBeanServer, objectName, "DoesNotExist");
3265

33-
assertThat(gauge.getValue())
34-
.isNull();
66+
assertThat(gauge.getValue()).isNull();
3567
}
68+
69+
@Test
70+
public void returnsNullIfMBeanNotFound() throws Exception {
71+
ObjectName objectName = new ObjectName("foo.bar:type=NoSuchMBean");
72+
JmxAttributeGauge gauge = new JmxAttributeGauge(mBeanServer, objectName, "LoadedClassCount");
73+
74+
assertThat(gauge.getValue()).isNull();
75+
}
76+
77+
@Test
78+
public void returnsAttributeForObjectNamePattern() throws Exception {
79+
ObjectName objectName = new ObjectName("JmxAttributeGaugeTest:name=test1,*");
80+
JmxAttributeGauge gauge = new JmxAttributeGauge(mBeanServer, objectName, "Value");
81+
82+
assertThat(gauge.getValue()).isInstanceOf(Long.class);
83+
assertThat((Long) gauge.getValue()).isEqualTo(Long.MAX_VALUE);
84+
}
85+
86+
@Test
87+
public void returnsNullIfObjectNamePatternAmbiguous() throws Exception {
88+
ObjectName objectName = new ObjectName("JmxAttributeGaugeTest:type=test,*");
89+
JmxAttributeGauge gauge = new JmxAttributeGauge(mBeanServer, objectName, "Value");
90+
91+
assertThat(gauge.getValue()).isNull();
92+
}
93+
94+
private static void registerMBean(ObjectName objectName) throws JMException {
95+
ObjectInstance objectInstance = mBeanServer.registerMBean(new JmxTest(), objectName);
96+
registeredMBeans.add(objectInstance.getObjectName());
97+
}
98+
3699
}

metrics-core/src/test/java/io/dropwizard/metrics/SharedMetricRegistriesTest.java

+50-1
Original file line numberDiff line numberDiff line change
@@ -8,14 +8,24 @@
88

99
import static org.assertj.core.api.Assertions.assertThat;
1010

11+
import java.lang.reflect.Field;
12+
import java.lang.reflect.Modifier;
13+
1114
public class SharedMetricRegistriesTest {
1215
@Before
1316
public void setUp() throws Exception {
17+
// Unset the defaultRegistryName field between tests for better isolation.
18+
final Field field = SharedMetricRegistries.class.getDeclaredField("defaultRegistryName");
19+
field.setAccessible(true);
20+
final Field modfiers = Field.class.getDeclaredField("modifiers");
21+
modfiers.setAccessible(true);
22+
modfiers.setInt(field, field.getModifiers() & ~Modifier.FINAL);
23+
field.set(null, null);
1424
SharedMetricRegistries.clear();
1525
}
1626

1727
@Test
18-
public void memoizesRegistriesByName() throws Exception {
28+
public void memorizesRegistriesByName() throws Exception {
1929
final MetricRegistry one = SharedMetricRegistries.getOrCreate("one");
2030
final MetricRegistry two = SharedMetricRegistries.getOrCreate("one");
2131

@@ -54,4 +64,43 @@ public void clearsRegistries() throws Exception {
5464
assertThat(SharedMetricRegistries.names())
5565
.isEmpty();
5666
}
67+
68+
@Test
69+
public void errorsWhenDefaultUnset() throws Exception {
70+
try {
71+
SharedMetricRegistries.getDefault();
72+
} catch (final Exception e) {
73+
assertThat(e).isInstanceOf(IllegalStateException.class);
74+
assertThat(e.getMessage()).isEqualTo("Default registry name has not been set.");
75+
}
76+
}
77+
78+
@Test
79+
public void createsDefaultRegistries() throws Exception {
80+
final String defaultName = "default";
81+
final MetricRegistry registry = SharedMetricRegistries.setDefault(defaultName);
82+
assertThat(registry).isNotNull();
83+
assertThat(SharedMetricRegistries.getDefault()).isEqualTo(registry);
84+
assertThat(SharedMetricRegistries.getOrCreate(defaultName)).isEqualTo(registry);
85+
}
86+
87+
@Test
88+
public void errorsWhenDefaultAlreadySet() throws Exception {
89+
try {
90+
SharedMetricRegistries.setDefault("foobah");
91+
SharedMetricRegistries.setDefault("borg");
92+
} catch (final Exception e) {
93+
assertThat(e).isInstanceOf(IllegalStateException.class);
94+
assertThat(e.getMessage()).isEqualTo("Default metric registry name is already set.");
95+
}
96+
}
97+
98+
@Test
99+
public void setsDefaultExistingRegistries() throws Exception {
100+
final String defaultName = "default";
101+
final MetricRegistry registry = new MetricRegistry();
102+
assertThat(SharedMetricRegistries.setDefault(defaultName, registry)).isEqualTo(registry);
103+
assertThat(SharedMetricRegistries.getDefault()).isEqualTo(registry);
104+
assertThat(SharedMetricRegistries.getOrCreate(defaultName)).isEqualTo(registry);
105+
}
57106
}

0 commit comments

Comments
 (0)