Custom Metrics
Send custom values from your Java project
Custom metrics let you submit project-specific values alongside the built-in platform metrics. Each custom metric must match a data source configured in your FastStats project settings.
Add Metrics
Attach custom metrics when you create the platform context:
import dev.faststats.bukkit.BukkitContext;
import dev.faststats.data.Metric;
import java.util.concurrent.atomic.AtomicInteger;
private final AtomicInteger arenaMatches = new AtomicInteger();
private final BukkitContext context = new BukkitContext.Factory(this, "YOUR_TOKEN")
.metrics(factory -> factory
.addMetric(Metric.number("arena_matches", arenaMatches::get))
.addMetric(Metric.stringArray("used_hooks", () -> new String[]{"Vault", "PlaceholderAPI"}))
.onFlush(() -> arenaMatches.set(0))
.create())
.create();
public void onEnable() {
context.ready();
}Metrics submission starts after context.ready() is called. Call
context.shutdown() during your platform's shutdown hook.
Data Source IDs
The first argument passed to Metric.* is the source ID. It must match the data
source ID in your FastStats project.
Metric.number("arena_matches", arenaMatches::get);Source IDs must use lowercase letters and underscores only, for example
arena_matches, used_hooks, or active_ruleset.
Supported Types
Use the factory method that matches the data source type:
| Method | Value type |
|---|---|
Metric.number(id, supplier) | Number |
Metric.string(id, supplier) | String |
Metric.bool(id, supplier) | Boolean |
Metric.numberArray(id, supplier) | Number[] |
Metric.stringArray(id, supplier) | String[] |
Metric.booleanArray(id, supplier) | Boolean[] |
Metric.numberMap(id, supplier) | Map<String, ? extends Number> |
Metric.stringMap(id, supplier) | Map<String, String> |
Metric.booleanMap(id, supplier) | Map<String, Boolean> |
Metric.number("active_arenas", () -> 4);
Metric.string("active_ruleset", () -> "ranked_duels");
Metric.bool("ranked_mode", () -> true);
Metric.stringArray("used_hooks", () -> new String[]{"Vault", "PlaceholderAPI"});
Metric.numberArray("match_durations", () -> new Number[]{310, 428, 275});
Metric.booleanArray("enabled_features", () -> new Boolean[]{true, false});Map metrics are useful when the set of keys is dynamic:
Metric.numberMap("hook_usage", () -> Map.of(
"vault", 12,
"placeholderapi", 4,
"luckperms", 9
));Supplier Behavior
Metric suppliers are called by the SDK during submission. Keep them:
- lightweight
- thread-safe
- side-effect free
- resilient to missing or temporarily unavailable platform state
Return null from a supplier when a value should be skipped for that
submission.
Metric.string("active_event", () -> currentEvent != null ? currentEvent.id() : null);If a value is expensive to compute, update a cached value from your own platform events and read the cached value from the metric supplier.
private volatile int cachedQueueDepth = 0;
public void updateQueueDepth() {
cachedQueueDepth = matchmakingQueue.size();
}
Metric.number("queue_depth", () -> cachedQueueDepth);Flush Callback
Use onFlush to clear counters after FastStats accepts a metrics submission.
private final AtomicInteger arenaMatches = new AtomicInteger();
.metrics(factory -> factory
.addMetric(Metric.number("arena_matches", arenaMatches::get))
.onFlush(() -> arenaMatches.set(0))
.create())Do not reset counters inside the metric supplier itself. Suppliers may be called without a successful submission.
Built-In Metrics Only
If you do not need custom metrics, create the metrics service without adding any metrics:
import dev.faststats.Metrics;
private final BukkitContext context = new BukkitContext.Factory(this, "YOUR_TOKEN")
.metrics(Metrics.Factory::create)
.create();
public void onEnable() {
context.ready();
}