Custom reporters

You can integrate custom reporters for Coda Hale Metrics. You do so by implementing a simple interface for the reporter lifecycle and enabling it through configuration. This section describes how to integrate custom reporters, and uses the SignalFx reporter as an example.

Cinnamon dependency

First make sure that your build is configured to use the Cinnamon Agent.

Add the Coda Hale Metrics plugin dependency to your build:

sbt
libraryDependencies += Cinnamon.library.cinnamonCHMetrics
Maven
<dependency>
  <groupId>com.lightbend.cinnamon</groupId>
  <artifactId>cinnamon-chmetrics</artifactId>
  <version>2.19.4</version>
</dependency>
Gradle
dependencies {
  implementation group: 'com.lightbend.cinnamon', name: 'cinnamon-chmetrics', version: '2.19.4'
}

Example: SignalFx reporter

This section will describe how to set up the SignalFx reporter for your project.

Reporter dependency

The first thing you need to do is to include the reporter of your choice to the build file:

sbt
libraryDependencies += "com.signalfx.public" % "signalfx-codahale" % "0.0.35"
Maven
<dependency>
    <groupId>com.signalfx.public</groupId>
    <artifactId>signalfx-codahale</artifactId>
    <version>0.0.35</version>
</dependency>
Gradle
dependencies {
    implementation group: 'com.signalfx.public', name: 'signalfx-codahale', version: '0.0.35'
}

Reporter interface

With the dependency in place the next step is to write the actual code that will be used to wire up the reporter. Lightbend Telemetry provides an API for this and the interface that you should implement is called Reporter.

public interface Reporter {
  /** Life cycle method called when telemetry is initiated. */
  void start();

  /** Life cycle method called when telemetry is stopped. */
  void stop();
}

Reporter imports

First we need to ensure that the required classes are imported.

Scala
import java.util.concurrent.TimeUnit

import com.codahale.metrics.MetricRegistry
import com.lightbend.cinnamon.chmetrics.reporter.{ Reporter, ReporterContext }
import com.signalfx.codahale.reporter.SignalFxReporter
Java
import com.codahale.metrics.MetricRegistry;
import com.lightbend.cinnamon.chmetrics.reporter.Reporter;
import com.lightbend.cinnamon.chmetrics.reporter.ReporterContext;
import com.signalfx.codahale.reporter.SignalFxReporter;

import java.util.concurrent.TimeUnit;

Imports explained:

  • Reporter : the contract interface that is used by Lightbend Telemetry when loading the external reporter
  • MetricRegistry : the Coda Hale metric registry containing the data that the reporter will traverse and report
  • ReporterContext : context information that contains com.typesafe.config.Config among other things (this is not a mandatory import - see constructors below)
  • SignalFxReporter : the reporter that you want to use - this will be different for each reporter

Reporter constructor

Lightbend Telemetry expects one of two constructors for a custom reporter. The constructor must accept a MetricRegistry, and can optionally accept a ReporterContext. In this example, the constructor uses both parameters:

Scala
class SignalFxMetricReporter(registry: MetricRegistry, context: ReporterContext) extends Reporter {
Java
public SignalFxMetricReporterJava(MetricRegistry registry, ReporterContext context) {
  init(registry, context);
}
Note

If no suitable constructor is available, an exception will be thrown and reporting will be disabled.

ReporterContext interface

The reporter context contains the following information that can come in handy when creating the reporter:

public interface ReporterContext {
  LoggingProvider loggingProvider();

  Config config();

  MetricFilter filter();

  ReporterMetadata metadata();
}
  • LoggingProvider: a handle to get to logging functionality
  • Config: contains the configuration used when initiating the system
  • MetricFilter: the filter associated with this reporter (the default filter if no filter has been defined in the configuration)

Initializing the reporter

As part of the initialization process you should set up the reporter. This section shows how to set up the SignalFx reporter, but how to go about will different for each reporter type.

Scala
private final val period = context.config.getInt("period")
private final val token = context.config.getString("auth-token")
var reporter: SignalFxReporter = new SignalFxReporter.Builder(registry, token).setFilter(context.filter).build()
Java
private void init(MetricRegistry registry, ReporterContext context) {
  period = context.config().getInt("period");
  String token = context.config().getString("auth-token");
  reporter = new SignalFxReporter.Builder(registry, token).setFilter(context.filter()).build();
}

Implementing the lifecycle methods

There are two lifecycle methods for the Reporter interface that should be implemented. Here we call the corresponding start and stop methods on the SignalFx reporter.

Scala
override def start(): Unit = {
  reporter.start(period, TimeUnit.SECONDS)
}

override def stop(): Unit = {
  reporter.stop()
  reporter = null
}
Java
@Override
public void start() {
  reporter.start(period, TimeUnit.SECONDS);
}

@Override
public void stop() {
  reporter.stop();
  reporter = null;
}

Full example

Here is the complete example for integrating a SignalFx reporter:

Scala
package sample.reporter

import java.util.concurrent.TimeUnit

import com.codahale.metrics.MetricRegistry
import com.lightbend.cinnamon.chmetrics.reporter.{ Reporter, ReporterContext }
import com.signalfx.codahale.reporter.SignalFxReporter

class SignalFxMetricReporter(registry: MetricRegistry, context: ReporterContext) extends Reporter {

  private final val period = context.config.getInt("period")
  private final val token = context.config.getString("auth-token")
  var reporter: SignalFxReporter = new SignalFxReporter.Builder(registry, token).setFilter(context.filter).build()

  override def start(): Unit = {
    reporter.start(period, TimeUnit.SECONDS)
  }

  override def stop(): Unit = {
    reporter.stop()
    reporter = null
  }
}
Java
package sample.reporter;

import com.codahale.metrics.MetricRegistry;
import com.lightbend.cinnamon.chmetrics.reporter.Reporter;
import com.lightbend.cinnamon.chmetrics.reporter.ReporterContext;
import com.signalfx.codahale.reporter.SignalFxReporter;

import java.util.concurrent.TimeUnit;


public class SignalFxMetricReporterJava implements Reporter {
  private SignalFxReporter reporter = null;
  private int period = 0;

  public SignalFxMetricReporterJava(MetricRegistry registry, ReporterContext context) {
    init(registry, context);
  }


  private void init(MetricRegistry registry, ReporterContext context) {
    period = context.config().getInt("period");
    String token = context.config().getString("auth-token");
    reporter = new SignalFxReporter.Builder(registry, token).setFilter(context.filter()).build();
  }


  @Override
  public void start() {
    reporter.start(period, TimeUnit.SECONDS);
  }

  @Override
  public void stop() {
    reporter.stop();
    reporter = null;
  }
}

Wire the reporter in configuration

The last part is to instruct Lightbend Telemetry to use the reporter. You do so by adding it to the configuration file (normally application.conf).

cinnamon.chmetrics {
  reporters += signalfx-reporter
}

cinnamon.akka {
  actors {
    "/user/*" = {
      report-by = class
    }
  }
}

signalfx-reporter {
  reporter-class = "sample.reporter.SignalFxMetricReporter"
  auth-token = "SIGNALFX_AUTH_TOKEN"
  period = 5
}

Now start your application and it will use the provided reporter to report metrics gathered by Lightbend Telemetry.