Java Futures

Lightbend Telemetry is capable of capturing data for Java CompletableFutures.

Note: Cinnamon only supports Java 8 or Java 11.

Cinnamon Java dependency

The Cinnamon Java module is a transitive dependency of the Cinnamon Akka module, so it will be included automatically.

Note: Java Future instrumentation is currently only supported in conjunction with Actor instrumentation, as the Java Future telemetry uses the metric backends as configured for an ActorSystem. An ActorSystem needs to be initialized before Java Future telemetry can be reported.

Java Future metrics

The following metrics are recorded for named Java CompletableFutures, type of metric in parentheses:

  • Created futures (rate) — the rate of CompletableFutures being created (for a given name).

  • Completing time (recorder) — the time from creation to completing the CompletableFuture.

  • Scheduled futures (recorder) — the number of futures currently scheduled (includes both future async sources and completion callbacks).

  • Scheduling time (recorder) — the time that futures/callbacks are in scheduling (in executor queues waiting to run).

  • Processed futures (rate) — the rate of futures/callbacks being processed (marked when the future/callback has finished running).

  • Processing time (recorder) — the time that futures/callbacks take to run.

To illustrate how future metrics are recorded, consider a thenApplyAsync operation on a CompletionStage. When thenApplyAsync is called, a new CompletableFuture is created for the result of the thenApplyAsync function and a completion callback is added to the original CompletableFuture. When the original CompletableFuture has completed, the thenApplyAsync callback is scheduled for execution. The callback will then run as determined by the executor, calling the thenApplyAsync function and completing the CompletableFuture with the result. The way that metrics are recorded for the CompletableFuture and completion callback are shown in this diagram:

Java Future configuration

Telemetry for Java Futures needs to be explicitly enabled, and CompletableFutures or CompletionStage callbacks need to be explicitly named in code, using a naming API provided by the Cinnamon Java module.

To enable telemetry for named Java Futures, create a cinnamon.conf file and enable the instrumentation setting: {
  future.instrumentation = on

Note: Java Future configuration must be specified in a cinnamon.conf file and will not be read from the application.conf.

Java Future naming API

Cinnamon includes a naming API to indicate CompletableFutures or CompletionStage callbacks that should be instrumented and to specify the identifier to use in telemetry.

Note: only named Java CompletableFutures will be instrumented.

For example, NameableCompletableFuture has a named alternative to CompletableFuture.supplyAsync which allows scheduled CompletableFutures to be instrumented:

import java.util.concurrent.CompletableFuture;

// this CompletableFuture is not instrumented
CompletableFuture<String> future =
    CompletableFuture.supplyAsync(() -> "compute all the things");

// this CompletableFuture is instrumented and named "compute"
CompletableFuture<String> instrumented =
    NameableCompletableFuture.supplyAsyncNamed("compute", () -> "compute all the things");

There is also a NameableCompletionStage with named alternatives for the CompletionStage callback operations. For example, to name and instrument a transform operation the thenApplyNamed method can be used in place of thenApply:

import java.util.concurrent.CompletionStage;

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "something");

CompletionStage<String> transformed =
        .thenApplyNamed("transform", value -> transform(value));

If you want to name some CompletableFutures or CompletionStage callbacks only for tracing, you can configure which Future names will be instrumented for metrics. Name selections can include a trailing wildcard.

For example, the following configuration will enable metrics for Future operations named foo and names starting with ba: {
  future.instrumentation = on

  future.metrics {
    names = ["foo", "ba*"]

The default is to record metrics for all named Futures.