A reporter for Dropwizard metrics to Amazon CloudWatch
Clone or download
Pull request Compare This branch is 14 commits ahead of blacklocus:master.
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Failed to load latest commit information.


CloudWatch integration for Dropwizard metrics

This is a fork of blacklocus/metrics-cloudwatch, which provides a subclass of ScheduledReporter from Dropwizard metrics (v3.x) that publishees to Amazon CloudWatch.

This fork decouples the CloudWatch publishing functionality from ScheduledReporter to give users more fine-grained control over when metrics are published to CloudWatch. This is useful for serverless applications where you cannot rely on the container to stay active between requests. In cases like this, it's better to use a different MetricRegistry instance for each request and report those metrics once at the end of the request.

Build Status

Table of Contents

Maven Dependency (Gradle)

Current Stable Release
repositories {

dependencies {
    compile 'com.jlhood:metrics-cloudwatch:1.0.1'

Other dependency formats on mvnrepository.com

Current Snapshot Release

repositories {
    maven {
        url 'https://oss.sonatype.org/content/repositories/snapshots/'

dependencies {
    compile 'com.jlhood:metrics-cloudwatch:1.0.2-SNAPSHOT'


Here is a bare minimum example of using the standalone CloudWatchReporter to report metrics on a per-request basis.

class MyOperation implements RequestHandler<I, O> {
    public O handleRequest(I request) {
        final MetricRegistry registry = new MetricRegistry();
        try {
            // process request
        } finally {
            new CloudWatchReporter(
                    new AmazonCloudWatchAsyncClient()

Here is a bare minimum example of using the ScheduledCloudWatchReporter to periodically publish metrics over time. Create a class to represent a namespace of metrics and provide methods enumerating the metrics recorded. The reporter interval is at 1 minute which will report new data every minute for the last minute to CloudWatch. 1 minute is the minimum resolution of CloudWatch. If you wanted to save money on API requests, you could go every 5 minutes or longer, keeping in mind that each data point to CloudWatch then represents 5 minutes, and you shouldn't view periods smaller than that in the CloudWatch console.

class ExampleMetrics {

    private final MetricRegistry registry = new MetricRegistry();

    public ExampleMetrics() {
        // CloudWatchReporter has many settings, see javadoc for details
        new ScheduledCloudWatchReporter(
                new CloudWatchReporter(
                        new AmazonCloudWatchAsyncClient()
                .start(1, TimeUnit.MINUTES);

    public void sentThatThing() {

    public void gotABatchOfThoseThingsYaSentMe(int count) {

public class ExampleApp {

    private final ExampleMetrics exampleMetrics;

    public ExampleApp(ExampleMetrics exampleMetrics) {
        this.exampleMetrics = exampleMetrics;

    public void sendAThing() {
        // ... somewhere in the code not so far away ...
    public void receiveSomeThings(List<Object> thoseThings) {
        // ... and so on ...

If you already have a Dropwizard metrics MetricsRegistry, you only need to give it to a ScheduledCloudWatchReporter start submitting all your existing metrics code to CloudWatch. Note that some symbols in the metric names have special meaning explained below.

In the test code, there is a test app that generates bogus metrics from two simulated machines (threads): ScheduledCloudWatchReporterTest.java

Metric types

The CloudWatch API speaks in terms of MetricDatum (AWS docs) and StatisticSet (AWS docs). Code Hale's metric classes are thus translated into these constructs in the most direct way possible. The metric classes are NOT reset, so that they retain their original, cumulative functionality. If you haven't you should also read all of the CloudWatch overview: CloudWatch Concepts (AWS docs). It will significantly ease understanding translations from the metrics classes to CloudWatch.

The CloudWatchReporter adds the metricType dimension as follows, and submits

Class Dimension Value sent statistic meaning per interval
Gauge gauge If numeric, .getValue() as MetricDatum. Non-numeric ignored.
Counter counterCount Diff in .getCount() since last report as MetricDatum.
Meter meterCount Diff in .getCount() since last report as MetricDatum.
Histogram histoSamples Diff in .getCount() (Histogram returns samples) since last report as MetricDatum.
histoStats† StatisticSet based on .getSnapshot().
Timer timerSamples Diff in .getCount() (Timer returns samples) since last report as MetricDatum.
timerStats† StatisticSet based on .getSnapshot(). sum / 1,000,000 (nanos -> millis)

The dimension name and values for each metric type are configurable in the CloudWatchReporterBuilder.

† - For histoStats and timerStats, you have to consider what the Snapshot actually is to understand how they are translated to StatisticSets. In a nutshell there is a sliding window of history. At each reporter interval all available values are read to compute the parts of a CloudWatch StatisticSet: the min, max, sum, average, and samples (number of data points).

If you plan on seriously using any of this at scale, you should apportion time to go read the code (CloudWatchReporter and Coda Hale metrics classes) to understand exactly what the metrics classes capture, and how that information gets translated into CloudWatch.

Special characters in metric names

Dimension support name=value: Any un-spaced tokens of the metric name that contain a '=' will be interpreted as CloudWatch dimensions. e.g. "CatCounter dev breed=calico" will result in a CloudWatch metric with Metric Name "CatCounter dev" and one Dimension { "breed": "calico" }.

Duplicate submission support token*: Neither the CloudWatch service nor web console will aggregate metrics across dimensions on custom metrics (see CloudWatch documentation). For convenience, we can just submit these metrics in duplicate, once with the dimension and once without (the aggregate over all values of this dimension).

The following example shows how you might name your metrics to submit them to CloudWatch to accomplish this.

Example metric naming

We have multiple machines in the Service X cluster. We want a count over all machines as well as counts for individual machines. To submit to each machine-specific and machine-ignornat, we name the counter ServiceX Requests machine={insert machine id here}.

In this example, this turns out to be

// machine A
metricRegistry.counter("ServiceX Requests machine=");
// machine B
metricRegistry.counter("ServiceX Requests machine=");

The = signifies a CloudWatch Dimension. This segment would thus be translated into a dimension with dimension name machine and dimension value or depending on the machine. Each machine submits one metric to CloudWatch. In the CloudWatch UI there would be only two metrics.

To get the aggregate of this metric over all machines, we would add all of the metrics regardless of machine together. Unfortunately, we use the CloudWatch web console, which will not aggregate across dimensions. So instead we submit the metric twice from each machine: once machine-specific with the machine dimension, and once without the machine dimension. The metric without the machine dimension represents performance across all machines.

The CloudWatchReporter provided by this project can do that by adding the * to any part of the name. In this case we want to permute the machine dimension so by appending * to the end of the machine dimension, the name string becomes

ServiceX Requests machine={insert machine id here}*

and this counter will now be sent twice for any machine. In our example we had two machines and So all of the metric submissions look like this across all machines.

  • machine submits to CloudWatch two metrics named
    • ServiceX Requests and
    • ServiceX Requests machine=
  • machine submits to CloudWatch two metrics named
    • ServiceX Requests and
    • ServiceX Requests machine=

Any name token can be permuted by appending it with *, not just dimensions. But be warned, permutations can grow exponentially such as in this example.

metricRegistry.counter("ServiceX Requests group-tag* machine=* strategy=dolphin* environment=development");

This resolves to all of the following CloudWatch metrics.

  • ServiceX Requests environment=development
  • ServiceX Requests environment=development machine=
  • ServiceX Requests environment=development strategy=dolphin
  • ServiceX Requests environment=development machine= strategy=dolphin
  • ServiceX Requests group-tag environment=development
  • ServiceX Requests group-tag environment=development machine=
  • ServiceX Requests group-tag environment=development strategy=dolphin
  • ServiceX Requests group-tag environment=development machine= strategy=dolphin

In case you forgot, AWS costs money. Metrics and monitoring can easily become the most expensive part of your stack. So be wary of metrics explosions.


git clone git@github.com:jlhood/metrics-cloudwatch
cd metrics-cloudwatch
./gradlew idea

Open the metrics-cloudwatch.ipr. Do NOT enable gradle integration in IntelliJ.


Copyright 2016 James Hood under the Apache 2.0 license