Micrometer StatsD

StatsD is a UDP-based sidecar-driven metrics collection system. The maintainer of the original StatsD line protocol specification is Etsy. Datadog’s DogStatsD and Influx’s Telegraf each accept a modified version of the line protocol, having each enriched the original specification with dimensionality in different ways.

If you intend to use the Datadog or Telegraf flavors, see the documentation for Micrometer’s Datadog or Influx support.

1. Installing

For Gradle, add the following implementation:

implementation 'io.micrometer:micrometer-registry-statsd:latest.release'

For Maven, add the following dependency:

<dependency>
  <groupId>io.micrometer</groupId>
  <artifactId>micrometer-registry-statsd</artifactId>
  <version>${micrometer.version}</version>
</dependency>

2. Configuring

This configuration is used to ship metrics to a StatsD agent that is compatible with the original Etsy protocol. Metrics are shipped immediately over UDP to the agent.

StatsdConfig config = new StatsdConfig() {
    @Override
    public String get(String k) {
        return null;
    }


    @Override
    public StatsdFlavor flavor() {
        return StatsdFlavor.Etsy;
    }
};

MeterRegistry registry = new StatsdMeterRegistry(config, Clock.SYSTEM);
You can also configure Telegraf to accept the dogstatsd format. If you use Telegraf, configuring Micrometer to ship Telegraf-formatted StatsD lines eases the requirements of your Telegraf configuration.

StatsdConfig is an interface with a set of default methods. If, in the implementation of get(String k), rather than returning null, you instead bind it to a property source, you can override the default configuration. For example, Micrometer’s Spring Boot support binds properties that are prefixed with management.metrics.export.statsd directly to the StatsdConfig:

management.metrics.export.statsd:
    flavor: etsy

    # You will probably want to conditionally disable StatsD publishing in local development.
    enabled: true

    # The interval at which metrics are sent to StatsD. The default is 1 minute.
    step: 1m

3. Customizing the Metrics Sink

By default, Micrometer publishes the StatsD line protocol over UDP, as the vast majority of existing StatsD agents are UDP servers. You can fully customize how the line protocol is shipped by modifying the builder for StatsdMeterRegistry:

Consumer<String> lineLogger = line -> logger.info(line); (1)

MeterRegistry registry = StatsdMeterRegistry.builder(StatsdConfig.DEFAULT) (2)
    .clock(clock)
    .lineSink(lineLogger)
    .build();
1 Define what to do with lines.
2 The flavor configuration option determines the structure of the line for the default line builder. It has no effect if you override the line builder with a customization.

3.1. Using Apache Kafka for Line Sink

You can also use Apache Kafka for line sink, as follows:

Properties properties = new Properties();
properties.setProperty(BOOTSTRAP_SERVERS_CONFIG, "localhost:9092");
properties.setProperty(KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());
properties.setProperty(VALUE_SERIALIZER_CLASS_CONFIG, StringSerializer.class.getName());

Producer<String, String> producer = new KafkaProducer<>(properties);

StatsdMeterRegistry.builder(statsdConfig)
        .lineSink((line) -> producer.send(new ProducerRecord<>("my-metrics", line)))
        .build();

Now Micrometer produces lines for metrics to the my-metrics topic and you can consume the lines on the topic.

4. Customizing the Line Format

The built-in Etsy, dogstatsd, and Telegraf flavors cover most known public StatsD agents, but you can completely customize the line format to satisfy closed, proprietary agents. Again, we use the StatsdMeterRegistry builder to establish a line builder for each ID. Providing an instance of the builder per ID offers you the opportunity to eagerly cache the serialization of the ID’s name and tags to optimize the serialization of a StatsD line based on that ID as samples are recorded. The following listing defines a fictional format:

Function<Meter.Id, StatsdLineBuilder> nameAndUnits = id -> new StatsdLineBuilder() {
    String name = id.getName() + "/" + (id.getBaseUnit() == null ? "unknown" : id.getBaseUnit());

    @Override
    public String count(long amount, Statistic stat) {
       return name + ":" + amount + "|c";
    }

    ... // implement gauge, histogram, and timing similarly
}

MeterRegistry registry = StatsdMeterRegistry.builder(StatsdConfig.DEFAULT) (1)
    .clock(clock)
    .lineBuilder(nameAndUnits)
    .build();
1 Because you have taken control of line building, the flavor is ignored.