7

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?


Update

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)
OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
smeeb
  • 27,777
  • 57
  • 250
  • 447

1 Answers1

9

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
Christian Trimble
  • 2,126
  • 16
  • 27
  • Thanks @C.Trimble (+1 - you're soo close!) - one super-quick followup question for the green check + bounty: *where do I actually configure the `MetricRegistry` and respective metrics reporter?* Thanks again! – smeeb Sep 18 '15 at 19:53
  • 1
    @smeeb does the information I added on configuration help? – Christian Trimble Sep 18 '15 at 20:19
  • Thanks again @C.Trimble (+1 again) - please see my update. Before `environment.metrics()` can return a value, I need to instantiate the `MetricRegistry` first - but where?!? Thanks again! – smeeb Sep 19 '15 at 00:32
  • ...Or is it that Dropwizard **creates** `environment.metrics()` for you? If that's the case, then how do you configure it and, say, specify an `Slf4jReporter` on it? – smeeb Sep 19 '15 at 08:34
  • Dropwizard manages that metrics registry for you. You just need to attach reporters to it. – Christian Trimble Sep 21 '15 at 20:30
  • Can I supply my own singleton metric registry to DropWizard or do I need to use the one DropWizard creates? – Jan-Olav Eide Oct 20 '15 at 19:45
  • It doesn't look like the `Environment` object provides a method for setting the registry. Why would you want to do that? – Christian Trimble Oct 20 '15 at 22:24
  • When I do that I get this error. It seems I can't use the environment in the GuiceBundle? `ERROR [2015-11-12 16:25:05,788] com.hubspot.dropwizard.guice.GuiceBundle: Exception occurred when creating Guice Injector - exiting ! com.google.inject.CreationException: Unable to create injector, see the following errors: ! ! 1) The dropwizard environment has not yet been set. This is likely caused by trying to access the dropwizard environment during the bootstrap phase. ! at com.hubspot.dropwizard.guice.DropwizardEnvironmentModule.providesEnvironment(DropwizardEnvironmentModule.java:36)` – Martin Charlesworth Nov 12 '15 at 16:34
  • I realised you can get the metrics registry from the bootstrap object, then pass it in to the bundle's constructor. – Martin Charlesworth Nov 12 '15 at 16:45
  • 1
    @martincharlesworth I have updated the code to include `guiceBundle = builder.build(Stage.DEVELOPMENT);`. This is needed to make singletons lazy, so that they initialize after the Environment is injected. This is a rough spot between the hubspot code and dropwizard. – Christian Trimble Nov 12 '15 at 21:05
  • @C.Trimble Thanks that works. Your code isn't quite right (you already built the guice bundle once) but I get the point. – Martin Charlesworth Nov 13 '15 at 23:11
  • @martincharlesworth I have corrected that error, thanks! – Christian Trimble Nov 13 '15 at 23:24