New: see how many SpigotMC page viewers end up running your plugin →

SDK API reference

Every public method of PluginAnalytics, the SDK's only class. Every method is safe to call at any time: nothing that goes wrong with analytics ever throws into your code, and every method does nothing when analytics is off.

Summary

MethodWhat it does
start(plugin, key)Starts collecting.
isEnabled()Whether analytics is running.
track(…)Counts an event (5 forms).
property(name, supplier)Reports a per-server value.
trackConfig(enabled)Turns automatic config.yml reading on or off.
excludeConfig(paths…)Leaves config paths out.
captureError(error)Reports an exception you caught.
logLevel(level)Lowest level of log lines collected.
time(name) · time(name, code)Measures a block of code.
autoPerformance(enabled)Turns sampling and automatic timing on or off.
updateNotices(enabled)Shows or hides update notices.
feedbackPrompts(enabled)Shows or hides the feedback prompt.
noticePermission(permission)Who receives notices on join.
feedback(sender, rating, message)Sends feedback from your command.
report(sender, message)Sends a problem report, with recent errors and logs.
suggest(sender, message)Sends a suggestion.
autoCommands(enabled)Turns the automatic report/suggest subcommands on or off.
commandBase(command)Which command gets report/suggest.
stop()Stops collecting.

Starting and stopping

static PluginAnalytics start(Plugin plugin, String key)

Starts collecting for your plugin and returns the instance to keep. Call it once, in onEnable. It never throws and never blocks. Analytics stays off (and every method does nothing) when the server owner disabled it, when the SDK wasn't relocated, or when the key doesn't start with pa_; the last two log a warning.

@Override
public void onEnable() {
    saveDefaultConfig();
    analytics = PluginAnalytics.start(this, "pa_your_key");
}

boolean isEnabled()

True when analytics is running: the server owner didn't turn it off, the SDK is relocated and the key is valid. Use it to hide a feedback command when it would do nothing.

if (!analytics.isEnabled()) sender.sendMessage("Feedback is turned off on this server.");

void stop()

Stops collecting: the background threads end and the SDK's log handlers and listeners are removed. It's called for you when your plugin is disabled, so you rarely need it. Data collected since the last report isn't sent.

Events

Names and property keys: lowercase letters, digits and _ . : -, up to 48 characters; invalid ones are ignored. Up to 5 properties, values cut at 48 characters. Events guide

void track(String event)

Counts something the server did.

analytics.track("arena_created");

void track(String event, String... properties)

Counts something the server did, with properties as key/value pairs.

analytics.track("arenas_reloaded", "trigger", "command");

void track(String event, Player player)

Counts something a player did. Player events power player funnels and reach.

analytics.track("game_started", player);

void track(String event, Player player, String... properties)

Counts something a player did, with properties as key/value pairs.

analytics.track("kit_selected", player, "kit", kit.getId());

void track(String event, Player player, Map<String, ?> properties)

Counts something a player did, with properties from a map. Values are turned into text with String.valueOf; null values are skipped.

analytics.track("game_finished", player, Map.of("mode", "solo", "result", "win"));

Configuration and properties

void property(String name, Supplier<?> value)

Reports a per-server value, read once an hour on the SDK's thread and sent when it changes (and once a day). Values are cut at 64 characters; null is skipped. Only read values that are safe off the main thread. Properties

analytics.property("economy", () -> economy != null ? "vault" : "none");

void trackConfig(boolean enabled)

Turns the automatic reading of your config.yml on (default) or off. Config usage

analytics.trackConfig(false);

void excludeConfig(String... paths)

Leaves config paths, and everything below them, out of Config usage. Case is ignored.

analytics.excludeConfig("discord", "storage.mysql");

Errors, logs and performance

void captureError(Throwable error)

Reports an exception you caught yourself. Uncaught exceptions that go through your plugin's code are reported automatically. Errors

try {
    database.save(arena);
} catch (SQLException e) {
    analytics.captureError(e);
}

void logLevel(Level level)

The lowest level of your plugin's log lines that is collected: Level.INFO (default), Level.WARNING, or Level.OFF for none. null is ignored. Logs

analytics.logLevel(Level.WARNING);

Span time(String name)

Starts measuring a block of code; close the returned Span (best with try-with-resources) when it's done. Shown in Performance with calls, average, p95 and max. The name follows the event naming rules. Spans

try (PluginAnalytics.Span span = analytics.time("arena_tick")) {
    tickArenas();
}

void time(String name, Runnable code)

Runs code right away and measures it.

analytics.time("load_arenas", () -> arenaManager.loadAll());

void autoPerformance(boolean enabled)

Turns main-thread sampling and the automatic timing of your event handlers and commands on (default) or off. Call it right after start; automatic command events stop with it. Details

analytics.autoPerformance(false);

Notices and feedback

void updateNotices(boolean enabled)

Shows (default) or hides the update notices you configure in the dashboard. Update notices

analytics.updateNotices(false);

void feedbackPrompts(boolean enabled)

Shows (default) or hides the feedback prompt you configure in the dashboard. Feedback

analytics.feedbackPrompts(false);

void noticePermission(String permission)

The permission that receives notices and prompts on join. The default is <plugin>.notify (your plugin's name in lowercase); operators always receive them. null or an empty string is ignored.

analytics.noticePermission("arenaplus.admin");

void feedback(CommandSender sender, int rating, String message)

Sends feedback to you from your own command. Rating 1 to 5, or 0 for none. It runs in the background, and the sender (if not null) is told whether it was sent, or that feedback is turned off on this server. Example command

analytics.feedback(sender, 5, "Great plugin!");

Reports and ideas

void report(CommandSender sender, String message)

Sends a problem report to you, with your plugin's errors and log lines from the last 15 minutes on that server. Runs in the background; the sender is told whether it was sent. One per person per minute. Reports & ideas

analytics.report(sender, "The arena doesn't reset after a game");

void suggest(CommandSender sender, String message)

Sends a suggestion to you. Same limits as report, without the errors and logs.

analytics.suggest(sender, "Add a spectator mode");

void autoCommands(boolean enabled)

Adds /<pluginname> report and suggest (default on). Turn it off to use report and suggest from your own commands.

analytics.autoCommands(false);

void commandBase(String command)

Puts report and suggest on one of your commands instead of /<pluginname>.

analytics.commandBase("arena"); // /arena report <text>

PluginAnalytics.Span

public static final class Span implements AutoCloseable, returned by time(name). Its only method, void close(), records the time since the span was created. Closing it twice records it twice.

Constants

public static final String SDK_VERSION: the SDK's version, "1.2.0", sent with every report.

System properties

JVM flags (-Dname=value) read by the SDK. They apply to every plugin using PluginAnalytics on that server.

PropertyEffect
pluginanalytics.firstDelaySeconds before the first report, instead of a random 3 to 6 minutes. For test servers: -Dpluginanalytics.firstDelay=10.
pluginanalytics.endpointThe URL reports are sent to, instead of https://pluginanalytics.dev/api/v1/ingest. Feedback goes to the same URL with /feedback in place of /ingest.
pluginanalytics.devAny value skips the relocation check, to run the SDK unrelocated in a development setup. Never ship a plugin that relies on it.