I am creating a Dropwizard Bundle to be reused across all my microservices. One of the things I would like to standardize on is the MetricRegistry
each service uses.
It would be great if I could I could configure each service to use the same MetricRegistry
by simply adding my bundle on init, so something like:
class Microservice1 extends Application<Microservice1Config> {
@Override
void initialize(Bootstrap<Microservice1Config> cfg) {
// Boom! Standardized metrics/reporters configured and initialized!
bootstrap.addBundle(new MyBundle())
}
}
The problem is that the Bundle
API doesn't seem to be conducive to this type of behavior:
class MyBundle implements Bundle {
MetricRegistry metricRegistry
@Override
void initialize(Bootstrap bootstrap) {
}
@Override
void run(Environment environment) {
environment.jersey().register(???)
}
}
Since the register(...)
method doesn't register metricRegistry
as a JAX-RS resource, I'm at a loss as to how to wire things up so that this metricRegistry
is used for all metrics throughout the entire microservice. Ideas?
What I'm looking for is where to put the following:
MetricRegistry metricRegistry = new MetricRegistry()
Slf4jReporter slf4jReporter = Slf4jReporter.forRegistry(metricRegistry)
.convertRatesTo(TimeUnit.SECONDS)
.convertDurationsTo(TimeUnit.SECONDS)
.build()
slf4jReporter.start(5, TimeUnit.SECONDS)
Well, there is a metric registry accessible from environment.metrics()
. There are lots of ways to get that injected where you need it. I use the dropwizard-guice bundle to add Guice support.
private GuiceBundle<MyConfiguration> guiceBundle;
@Override
public void initialize(Bootstrap<MyConfiguration> bootstrap) {
guiceBundle = GuiceBundle.<MyConfiguration>newBuilder()
.addModule(new MyModule())
.setConfigClass(MyConfiguration.class)
.build(Stage.DEVELOPMENT);
bootstrap.addBundle(guiceBundle);
}
Then I create a provider rule in the application's module for the metric registry.
public class MyModule extends AbstractModule {
...
@Provides
@Singleton
public MetricRegistry provideRegistry( Environment environment ) {
return environment.metrics();
}
}
And in my resource, I mark the registry as injected.
@Path("/resource")
public class MyResource {
@Inject
MetricRegistry registry;
}
Finally, I am requesting my static resources from Guice's injector and then passing an instance to Jersey, instead of a class name.
@Override
public void run(Environment environment) {
Injector injector = guiceBundle.getInjector();
environment.jersey().register(injector.getInstance(MyResource.class));
}
You could probably just register the class name and rely on the Guice/HK2 bridge to provide the injection, but historically Guice/HK2 integration has been problematic.
Once you have the metric registry wired up, you will need to configure metrics to send data somewhere. You can do this with the metrics configuration block.
For example, if you would like to send metrics to graphite, you can set up the graphite metrics reporter. You will need to add a dependency on the graphite reporter.
<dependency>
<artifactId>dropwizard-metrics-graphite</artifactId>
<groupId>io.dropwizard</groupId>
<version>${dropwizard.version}</version>
</dependency>
and add configuration for the reporter.
metrics:
reporters:
- type: graphite
prefix: <PREFIX_FOR_METRICS>
host: <GRAPHITE_HOST>
port: <GRAPHITE_PORT>
frequency: 10s