package com.prometheus.spigot; import com.prometheus.spigot.http.MetricsHttpHandler; import com.prometheus.spigot.listeners.BlockBreakListener; import com.prometheus.spigot.listeners.ChatListener; import com.prometheus.spigot.listeners.ChunkListener; import com.prometheus.spigot.listeners.CommandListener; import com.prometheus.spigot.listeners.ConnectionListener; import com.prometheus.spigot.listeners.DeathListener; import com.prometheus.spigot.listeners.MoveListener; import com.prometheus.spigot.listeners.PluginListener; import com.prometheus.spigot.metrics.MetricsRegistry; import com.prometheus.spigot.util.TokenGenerator; import com.sun.net.httpserver.HttpServer; import java.io.IOException; import java.net.InetSocketAddress; import java.util.Locale; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import org.bukkit.Bukkit; import org.bukkit.plugin.java.JavaPlugin; import org.bukkit.scheduler.BukkitTask; public final class PrometheusSpigotPlugin extends JavaPlugin { private HttpServer httpServer; private ExecutorService httpExecutor; private MetricsRegistry metricsRegistry; private String bearerToken; private boolean countBlockMovementOnly; private BukkitTask snapshotTask; @Override public void onEnable() { saveDefaultConfig(); bearerToken = getConfig().getString("http.bearer_token", "").trim(); if (bearerToken.isBlank()) { bearerToken = TokenGenerator.generate(); getConfig().set("http.bearer_token", bearerToken); saveConfig(); } boolean enableJvm = getConfig().getBoolean("metrics.enable_jvm", true); boolean enableProcess = getConfig().getBoolean("metrics.enable_process", true); countBlockMovementOnly = getConfig().getBoolean("movement.count_block_changes_only", true); metricsRegistry = new MetricsRegistry(getServer(), enableJvm, enableProcess); scheduleSnapshotTask(); registerListeners(); startHttpServer(); } @Override public void onDisable() { if (httpServer != null) { httpServer.stop(0); httpServer = null; } if (httpExecutor != null) { httpExecutor.shutdownNow(); httpExecutor = null; } if (snapshotTask != null) { snapshotTask.cancel(); snapshotTask = null; } } private void registerListeners() { var pluginManager = getServer().getPluginManager(); pluginManager.registerEvents(new BlockBreakListener(metricsRegistry), this); pluginManager.registerEvents(new DeathListener(metricsRegistry), this); pluginManager.registerEvents(new ChatListener(metricsRegistry), this); pluginManager.registerEvents(new MoveListener(metricsRegistry, countBlockMovementOnly), this); pluginManager.registerEvents(new ConnectionListener(metricsRegistry), this); pluginManager.registerEvents(new CommandListener(metricsRegistry), this); pluginManager.registerEvents(new ChunkListener(metricsRegistry), this); pluginManager.registerEvents(new PluginListener(metricsRegistry), this); } private void startHttpServer() { String bindAddress = getConfig().getString("http.bind_address", "0.0.0.0").trim(); int port = getConfig().getInt("http.port", 9149); String path = getConfig().getString("http.path", "/metrics").trim(); if (!path.startsWith("/")) { path = "/" + path; } InetSocketAddress socketAddress = new InetSocketAddress(bindAddress, port); try { httpServer = HttpServer.create(socketAddress, 0); } catch (IOException ex) { getLogger().severe(String.format(Locale.ROOT, "Failed to start HTTP server on %s:%d", bindAddress, port)); getLogger().severe(ex.getMessage()); return; } httpExecutor = Executors.newFixedThreadPool(2); httpServer.setExecutor(httpExecutor); httpServer.createContext(path, new MetricsHttpHandler(metricsRegistry, bearerToken)); httpServer.start(); getLogger().info(String.format(Locale.ROOT, "Prometheus endpoint listening on http://%s:%d%s", bindAddress, port, path)); } private void scheduleSnapshotTask() { int intervalSeconds = getConfig().getInt("metrics.entity_snapshot_interval_seconds", 15); long intervalTicks = Math.max(20L, intervalSeconds * 20L); metricsRegistry.refreshEntitySnapshots(); snapshotTask = Bukkit.getScheduler().runTaskTimer(this, metricsRegistry::refreshEntitySnapshots, intervalTicks, intervalTicks); } }