Add metrics at runtime
Record method timings on a runtime proxy through Dropwizard Metrics and read them back.
Problem
You want timing data on every method call through a proxy — how often it runs, how long it takes, distribution percentiles — without touching the implementation and without standing up a separate instrumentation layer.
addMetrics() on the builder wires Dropwizard Metrics into the proxy. From
then on, every invocation is timed, and the metrics show up in
RapidPMMetricsRegistry.
Files involved
End-to-end metrics test
@Test
public void testAddMetrics() throws Exception {
final InnerDemoClass original = new InnerDemoClass();
final InnerDemoInterface demoLogic = DynamicProxyBuilder
.createBuilder(InnerDemoInterface.class, original)
.addSecurityRule(() -> true)
.addSecurityRule(() -> true)
.addMetrics()
.build();
Assertions.assertNotNull(demoLogic);
Assertions.assertEquals(demoLogic.doWork(), original.doWork());
final MetricRegistry metrics = RapidPMMetricsRegistry.getInstance().getMetrics();
final ConsoleReporter reporter = ConsoleReporter.forRegistry(metrics)
.convertRatesTo(TimeUnit.NANOSECONDS)
.convertDurationsTo(TimeUnit.MILLISECONDS)
.build();
reporter.start(1, TimeUnit.SECONDS);
IntStream.range(0, 10_000_000).forEach(i -> {
final String s = demoLogic.doWork();
workingHole(s.toUpperCase());
});
logger().info("s1 = {}", s1);
final SortedMap<String, Histogram> histograms = metrics.getHistograms();
Assertions.assertNotNull(histograms);
Assertions.assertFalse(histograms.isEmpty());
Assertions.assertTrue(histograms.containsKey(InnerDemoInterface.class.getName() + ".doWork"));
}From proxybuilder tag 00.10.00
There are three things happening in this test, intentionally combined to show the full flow.
- Wrap the implementation.
DynamicProxyBuilder.createBuilder(...)takes the interface and the instance..addSecurityRule(() -> true)is present twice to exercise rule composition; both returntrue, so the call is never blocked..addMetrics()is the line that turns on timing collection..build()produces the proxy. - Set up a console reporter.
RapidPMMetricsRegistry.getInstance()exposes the sharedMetricRegistry. AConsoleReporteris attached to it, printing collected metrics once per second on the test’s stdout. - Generate load. Ten million calls to
demoLogic.doWork()flow through the proxy. Each call is timed. After the loop, the test reads the resulting histogram out of the registry and asserts that it contains an entry keyed byInnerDemoInterface.class.getName() + ".doWork".
Run it
./mvnw -pl testusage test -Dtest=DynamicProxyBuilderTest#testAddMetrics
The console reporter prints histograms during the run. Expect output similar to:
-- Histograms ----------------------------------------------------------------
junit.com.svenruppert.proxybuilder.proxy.dynamic.InnerDemoInterface.doWork
count = 9876543
min = 0
max = 47
mean = 0.13
median = 0.00
75% <= 0.00
95% <= 0.00
98% <= 1.00
99% <= 2.00
99.9% <= 6.00
Exact numbers depend on the host, but the histogram entry will be there.
Reading metrics back programmatically
The test grabs the histogram and asserts on it:
final SortedMap<String, Histogram> histograms = metrics.getHistograms();
Assertions.assertTrue(histograms.containsKey(
InnerDemoInterface.class.getName() + ".doWork"));
Use the same pattern in your application to drive alerts, dashboards or custom reporters. The keys are derived from the fully qualified interface name plus the method name, so overloads are distinguishable.
When you would prefer compile-time metrics
addMetrics() is convenient when you build the proxy once and hand it off.
If you want timings without runtime reflection — for GraalVM native image,
for startup-time reasons, or because the metrics class should show up by
name in stack traces — see
@StaticMetricsProxy instead.