StatsD client Java library

Overview

The StatsD client is a Java library used for recording custom application metrics and JVM metrics.

It is intended to be used with the Collection Agent and the StatsD plug-in. The workflow is as follows:

  1. The StatsD client sends metrics via UDP or TCP to the agent.
  2. The agent aggregates and reports the metrics every 10 seconds by default.

Specification

The StatsD client is based on the StatsD protocol with the following enhancements:

  • Custom event reporting.
  • Custom attribute message type for sending static non-metric information.
  • Dimensions (tags) can be added to all metrics or to specific metrics.
  • Unit of measure can be specified per metric.

Custom application metrics

Usage

To add as a Maven dependency:

  1. Define the ITRS Maven repository in pom.xml or ~/.m2/settings.xml.
  2. <repositories>
      <repository>
        <id>itrsgroup-collection</id>
        <url>https://itrsgroup.bintray.com/collection</url>
        <snapshots>
          <enabled>false</enabled>
        </snapshots>
      </repository>
    </repositories>
    
  3. Add the dependency in pom.xml.
  4. <dependency>
      <groupId>com.itrsgroup.collection</groupId>
      <artifactId>statsd-client</artifactId>
      <version>VERSION</version>
    </dependency>

Note: You can also file the installation files at ITRS Downloads.

A single StatsD client instance can be shared throughout an application. It is safe for concurrent use by multiple threads.

After constructing an instance, the client does not throw any exceptions, and none of its methods will block. Any exceptions that are caught internally are reported through an optional error callback.

Creating a client:

// Create a client builder:
final StatsdClientBuilder builder = new StatsdClientBuilder();

// By default, UDP is used. To use TCP instead:
builder.useTcp();

// Use a server and port different from the default of localhost:8125:
builder.server(InetAddress.getByName("host.domain.com"));
builder.port(9000);

// Set an optional error callback:
builder.errorCallback(ex -> logger.error("statsd error", ex));

// Add dimension(s) to all metrics:
builder.defaultDimension("region", "Americas");

// Build the client:
final StatsdClient statsd = builder.build();

Recording metrics:

// Increment a counter
statsd.increment("failed_logins");

// Decrement a counter
statsd.decrement("thread_count");

// Adjust a counter
statsd.count("items_processed", 5);

// Set a gauge with an absolute value
statsd.gauge("cache_size", 123.5);

// Set a gauge with a unit of measure
statsd.gauge("cache_size", 52.5, Unit.MEGABYTES);

// Adjust a gauge
statsd.gauge("tank", -5.0);

// Register periodic gauge sampling.
// The given DoubleSupplier will be polled at the client's configured interval.  This should
// only be called once per name/dimension combination - duplicate registrations are ignored.
statsd.gauge("cache_size", () -> this::getCacheSize);

// Count unique values in a bucket
statsd.unique("unique_logins", "alice");

// Record a timer in millseconds
statsd.timer("db_query_time", 56L);

// Record a timer with a different unit
statsd.timer("db_query_time", 56L, TimeUnit.MICROSECONDS);

// Include dimensions with a metric (to reduce garbage, it is recommended to create the dimensions once and reuse)
statsd.increment("failed_logins", Collections.singleton(Dimension.of("region", "Europe")));

// Send an event
statsd.event("custom_event", "event description", Severity.INFO);

// Send a static attribute.
statsd.attribute("app_version", "1.5"); 

// Close the client when no longer needed
statsd.close();

StatsD server

If the server/port of the StatsD server are not explicitly provided when building a client (see example in Usage), the builder looks for the STATSD_SERVER and STATSD_PORT environment variables. If these are not present, the client defaults to localhost:8125.

Add dimensions

There are three ways to add dimensions to a metric:

  • Default dimensions — these are custom dimensions applied to every metric. They can be defined one of two ways:
    • Via code:
      builder.defaultDimension("region", "Americas");
    • Via environment variables:
      export STATSD_DIMENSION_region=Americas
  • Per-metric dimensions — these are custom dimensions specified for one metric, for example:
    statsd.increment("failed_logins", Dimension.of("region", "europe"));
    
  • Platform dimensions — the StatsD client creates dimensions from specific environment variables based on the deployment platform. This feature can be disabled by building the client with .disablePlatformDimensions() or by setting the STATSD_DISABLE_PLATFORM_DIMENSIONS environment variable to true.
    • The variables for Kubernetes and OpenShift are:
      VariableDimension
      HOSTNAMEhostname

      POD_NAME

      pod_name
      CONTAINER_NAMEcontainer_name
      NAMESPACEnamespace
    • The variables for Pivotal Cloud Foundry are:
      VariableDimension
      VCAP_APPLICATION.application_nameapplication_name
      VCAP_APPLICATION.space_name space_name
      CF_INSTANCE_GUIDcf_instance_guid
      CF_INSTANCE_INDEXcf_instance_index
      CF_INSTANCE_INTERNAL_IPcf_instance_internal_ip
      CF_INSTANCE_IPcf_instance_ip
      HOSTNAMEhostname
    • Default variables are (if no other platform type is selected):
      VariableDimension
      APP_NAMEapp_name
      HOSTNAMEhostname

Sampling rate

When sampling high-frequency operations, it is possible to flood the StatsD server with a large number of messages.

To mitigate this problem, some operations can use a sampling rate which causes the client to send a message for only a specified percentage of all invocations.

Example:

void frequentlyInvokedMethod() {
  statsd.increment("metric", 0.5);
}

The client sends the increment message for approximately half of the times it is invoked. In those instances, the client includes the sampling rate in the message: metric:1|c|@0.5. This instructs the server to multiply the value 1 by 2 to approximate the actual value.

Note: This feature is only available for counters and timers.

Sets

To count the number of unique items in a bucket or set, follow this example:

void login(String username) {
  statsd.unique("unique_logins", username, Collections.singleton(Dimension.of("region", "europe")));
}

The StatsD server tracks the number of unique values per reporting interval and publishes them as a counter metric.

Collect JVM metrics

The JvmMetrics class collects metrics about the JVM itself. By default, collection occurs every 10 seconds and the metrics are reported through a StatsD client.

Custom collectors can be created as needed. See the JvmMetricsCollector interface and the usage example.

There are two ways in which you can instrument an application:

  • Create a metrics collector by adding code to the application.
  • Invoke the collector through the -javaagent JVM runtime argument.

Create a metrics collector

You can create the metrics collector by adding the following code to the application:

final JvmMetricsBuilder builder = new JvmMetricsBuilder();
  
// Create a statsd client (see previous section for details)
final MetricsClient client = new StatsdClientBuilder().build();
builder.client(client);
   
// By default, exceptions will be silently swallowed.  Optionally provide a way to log them.
builder.errorCallback(Throwable::printStackTrace);
    
// By default, all categories of metrics will be collected.  If only specific categories are desired:
builder.collectMemoryMetrics();
builder.collectOperatingSystemMetrics();
builder.collectRuntimeMetrics();
builder.collectThreadMetrics();
   
// Add a custom collector
builder.addCollector(new MyCollector());

// Override the default number of worker threads (this should never exceed the number of collectors)
builder.workerThreads(5);

// Finish building
final JvmMetrics metrics = builder.build();
   
// If/when the metrics are no longer needed
metrics.close();

Once the collector is instantiated, the metrics collection begins and no further interaction with this class is required. The collector reports exceptions through an optionally provided callback.

Note: Only one JvmMetrics instance can be created per JVM.

Invoke a metrics collector

You can invoke the collector with the -javaagent JVM runtime argument. This allows metrics collection to be enabled on any existing application without the need to modify code.

The agent creates its own StatsD client used to deliver metrics to the Collection Agent. The client's default destination is localhost:8125. This can be overridden via the STATSD_SERVER and STATSD_PORT environment variables.

Example:

java -javaagent:statsd-client-VERSION.jar[=option=val,option=val,...] -jar myapplication.jar

The following options are available:

  • protocol=[udp|tcp] — transport protocol (defaults to UDP).
  • collect=[memory|os|runtime|threading] — enables collection of a metric category. By default, all categories are enabled. Setting at least one collect option overrides the default. It can be specified multiple times.
  • dimension=key:val — adds a default dimension. It can be specified multiple times.
  • reportingInterval=10000 — specifies the reporting interval in milliseconds.
  • maxMessageSize=1432 — specifies the maximum StatsD message size in bytes.
  • workerThreads=2 — specifies a number of collector threads.
  • log— enables error logging. Exceptions will be printed to stderr.

If you specify invalid arguments, the agent exits the JVM with an exit code of 1.

Collected metrics

Metrics collected for memory

Metric name Type Unit Dimensions
jvm_memory_heap_used gauge bytes  
jvm_memory_heap_committed gauge bytes  
jvm_memory_heap_max gauge bytes  
jvm_memory_non_heap_used gauge bytes  
jvm_memory_non_heap_committed gauge bytes  
jvm_memory_non_heap_max gauge bytes  
jvm_memory_pool_heap_used gauge bytes jvm_memory_pool_name=pool_name
jvm_memory_pool_heap_committed gauge bytes jvm_memory_pool_name=pool_name
jvm_memory_pool_heap_max gauge bytes jvm_memory_pool_name=pool_name
jvm_memory_pool_non_heap_used gauge bytes jvm_memory_pool_name=pool_name
jvm_memory_pool_non_heap_committed gauge bytes jvm_memory_pool_name=pool_name
jvm_memory_pool_non_heap_max gauge bytes jvm_memory_pool_name=pool_name
jvm_memory_gc_collection_count gauge none jvm_memory_gc_name=collector_name
jvm_memory_gc_collection_time gauge milliseconds jvm_memory_gc_name=collector_name

Metrics collected for threads

Metric name Type Unit Dimensions
jvm_threads gauge none  
jvm_threads_daemon gauge none  
jvm_threads_peak gauge none  
jvm_threads_started gauge none  
jvm_threads_states gauge none thread_state=[NEW,RUNNABLE,etc...]
jvm_threads_monitor)deadlock gauge none  

Metrics collected for runtime

Metric name Type Unit
jvm_runtime_start_time gauge milliseconds
jvm_runtime_uptime gauge milliseconds
jvm_runtime_name attribute none
jvm_runtime_vm_vendor attribute none
jvm_runtime_vm_version attribute none
jvm_runtime_spec_name attribute none
jvm_runtime_spec_vendor attribute none
jvm_runtime_spec_version attribute none
jvm_runtime_class_path attribute none
jvm_runtime_library_path attribute none

Metrics collected for operating system

Metric name Type Unit
jvm_os_system_load_average attribute none
jvm_os_name attribute none
jvm_os_arch attribute none
jvm_os_version attribute none
jvm_os_available_processors attribute none