Placeholders¶
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:
- Optional dimensions that can be conditionally enabled.
- 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.
Dependencies¶
To use the placeholders support add a dependency on:
com.netflix.spectator:spectator-ext-placeholders:0.101.0
Usage¶
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
.getProperty("server.detailedMetricsEnabled")
.asBoolean(false);
// 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) {
fillInContext(request);
Response response = process(request);
fillInContext(response);
// Update the counter, the placeholders will be resolved when the activity, in
// this case the increment is called.
rps.increment();
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) {
this.name = 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;
}
}
}