Skip to content


The placeholders extension allows for identifiers to be created with dimensions that will get filled in based on the context when an activity occurs. The primary use-cases are to support:

  1. Optional dimensions that can be conditionally enabled.
  2. Pulling dimensions from another context such as a thread local store. This can make it is easier to share the across various parts of the code.


To use the placeholders support add a dependency on:


Placeholder support is available for activity based types including counters, timers, and distribution summaries. To get started create a PlaceholderFactory from the registry:

PlaceholderFactory factory = PlaceholderFactory.from(registry);

Then use the factory to create an identifier using a TagFactory to dynamically fetch the value for a given dimension when some activity occurs. Suppose we want to use a dynamic configuration library such as Archaius to conditionally enable a dimension with high cardinality:

public class Server {

  private final Context context;
  private final Counter rps;

  public Server(Context context, PropertyFactory props, Registry registry) {
    this.context = context;

    // Property that can be dynamically updated to indicate whether or not
    // detailed dimensions should be added to metrics.
    Property<Boolean> enabled = props

    // Factory for creating instances of the counter using placeholders
    PlaceholderFactory factory = PlaceholderFactory.from(registry);

    // Create the underlying id with 4 possible dimensions:
    // *  method and status - low cardinality and always added if available
    //    in the context.
    // *  geo and device - high cardinality and only available if the property
    //    to enable detailed metrics is set to true.
    PlaceholderId rpsId = factory.createId("server.requests")
      .withTagFactory(TagFactory.from("method", context::getMethod))
      .withTagFactory(TagFactory.from("status", context::getStatus))
      .withTagFactory(new DetailedDimension("geo", enabled, context::getGeo))
      .withTagFactory(new DetailedDimension("device", enabled, context::getDevice));
    rps = factory.counter(rpsId);

  public Response handle(Request request) {
    Response response = process(request);

    // Update the counter, the placeholders will be resolved when the activity, in
    // this case the increment is called.
    return response;

  // Tag factory that can be controlled with an enabled property.
  private static class DetailedDimension implements TagFactory {

    private final String name;
    private final Supplier<String> valueFunc;

    DetailedDimension(String name, Property<Boolean> enabled, Supplier<String> valueFunc) { = name;
      this.enabled = enabled;
      this.valueFunc = valueFunc;

    @Override public String name() {
      return name;

    @Override public Tag createTag() {
      return enabled.get()
          ? new BasicTag(name, valueFunc.get())
          : null;