feat: add prometheus plugin scaffold
This commit is contained in:
@@ -0,0 +1,155 @@
|
||||
package com.prometheus.spigot.metrics;
|
||||
|
||||
import java.lang.management.ManagementFactory;
|
||||
import java.lang.management.MemoryUsage;
|
||||
import java.lang.management.ThreadMXBean;
|
||||
import java.util.Comparator;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.LongAdder;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.Server;
|
||||
import org.bukkit.event.entity.EntityDamageEvent;
|
||||
|
||||
public final class MetricsRegistry {
|
||||
private final Server server;
|
||||
private final boolean enableJvm;
|
||||
private final boolean enableProcess;
|
||||
private final long startTimeMillis;
|
||||
|
||||
private final LongAdder chatMessages = new LongAdder();
|
||||
private final LongAdder playerMovements = new LongAdder();
|
||||
private final ConcurrentHashMap<String, LongAdder> blocksBrokenByMaterial = new ConcurrentHashMap<>();
|
||||
private final ConcurrentHashMap<String, LongAdder> deathsByCause = new ConcurrentHashMap<>();
|
||||
|
||||
public MetricsRegistry(Server server, boolean enableJvm, boolean enableProcess) {
|
||||
this.server = server;
|
||||
this.enableJvm = enableJvm;
|
||||
this.enableProcess = enableProcess;
|
||||
this.startTimeMillis = System.currentTimeMillis();
|
||||
}
|
||||
|
||||
public void incrementChatMessages() {
|
||||
chatMessages.increment();
|
||||
}
|
||||
|
||||
public void incrementPlayerMovements() {
|
||||
playerMovements.increment();
|
||||
}
|
||||
|
||||
public void incrementBlocksBroken(Material material) {
|
||||
incrementLabeledCounter(blocksBrokenByMaterial, material.name());
|
||||
}
|
||||
|
||||
public void incrementPlayerDeath(EntityDamageEvent.DamageCause cause) {
|
||||
incrementLabeledCounter(deathsByCause, cause.name());
|
||||
}
|
||||
|
||||
public void incrementPlayerDeath(String cause) {
|
||||
incrementLabeledCounter(deathsByCause, cause);
|
||||
}
|
||||
|
||||
public String render() {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
appendCounter(builder, "spigot_chat_messages_total", "Total chat messages sent", chatMessages.sum());
|
||||
appendCounter(builder, "spigot_player_movements_total", "Total player movement events", playerMovements.sum());
|
||||
|
||||
appendLabeledCounter(builder, "spigot_blocks_broken_total", "Blocks broken by material", "material", blocksBrokenByMaterial);
|
||||
appendLabeledCounter(builder, "spigot_player_deaths_total", "Player deaths by cause", "cause", deathsByCause);
|
||||
|
||||
appendGauge(builder, "spigot_players_online", "Online player count", server.getOnlinePlayers().size());
|
||||
appendGauge(builder, "spigot_players_max", "Max player slots", server.getMaxPlayers());
|
||||
appendGauge(builder, "spigot_worlds_loaded", "Loaded world count", server.getWorlds().size());
|
||||
|
||||
if (enableProcess) {
|
||||
appendProcessMetrics(builder);
|
||||
}
|
||||
|
||||
if (enableJvm) {
|
||||
appendJvmMetrics(builder);
|
||||
}
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
private void appendProcessMetrics(StringBuilder builder) {
|
||||
double uptimeSeconds = (System.currentTimeMillis() - startTimeMillis) / 1000.0;
|
||||
appendGauge(builder, "process_uptime_seconds", "Process uptime in seconds", formatDouble(uptimeSeconds));
|
||||
}
|
||||
|
||||
private void appendJvmMetrics(StringBuilder builder) {
|
||||
MemoryUsage heap = ManagementFactory.getMemoryMXBean().getHeapMemoryUsage();
|
||||
MemoryUsage nonHeap = ManagementFactory.getMemoryMXBean().getNonHeapMemoryUsage();
|
||||
appendMemoryMetrics(builder, "heap", heap);
|
||||
appendMemoryMetrics(builder, "nonheap", nonHeap);
|
||||
|
||||
ThreadMXBean threadBean = ManagementFactory.getThreadMXBean();
|
||||
appendGauge(builder, "jvm_threads", "Current JVM thread count", threadBean.getThreadCount());
|
||||
}
|
||||
|
||||
private void appendMemoryMetrics(StringBuilder builder, String area, MemoryUsage usage) {
|
||||
appendLabeledGauge(builder, "jvm_memory_used_bytes", "JVM memory used in bytes", "area", area, usage.getUsed());
|
||||
if (usage.getMax() >= 0) {
|
||||
appendLabeledGauge(builder, "jvm_memory_max_bytes", "JVM memory max in bytes", "area", area, usage.getMax());
|
||||
}
|
||||
}
|
||||
|
||||
private void appendCounter(StringBuilder builder, String name, String help, long value) {
|
||||
appendHelpAndType(builder, name, help, "counter");
|
||||
builder.append(name).append(' ').append(value).append('\n');
|
||||
}
|
||||
|
||||
private void appendGauge(StringBuilder builder, String name, String help, long value) {
|
||||
appendHelpAndType(builder, name, help, "gauge");
|
||||
builder.append(name).append(' ').append(value).append('\n');
|
||||
}
|
||||
|
||||
private void appendGauge(StringBuilder builder, String name, String help, String value) {
|
||||
appendHelpAndType(builder, name, help, "gauge");
|
||||
builder.append(name).append(' ').append(value).append('\n');
|
||||
}
|
||||
|
||||
private void appendLabeledCounter(StringBuilder builder, String name, String help, String label, Map<String, LongAdder> values) {
|
||||
appendHelpAndType(builder, name, help, "counter");
|
||||
values.entrySet().stream()
|
||||
.sorted(Map.Entry.comparingByKey(Comparator.naturalOrder()))
|
||||
.forEach(entry -> builder.append(name)
|
||||
.append('{').append(label).append("=\"")
|
||||
.append(escapeLabelValue(entry.getKey()))
|
||||
.append("\"} ")
|
||||
.append(entry.getValue().sum())
|
||||
.append('\n'));
|
||||
}
|
||||
|
||||
private void appendLabeledGauge(StringBuilder builder, String name, String help, String label, String labelValue, long value) {
|
||||
appendHelpAndType(builder, name, help, "gauge");
|
||||
builder.append(name)
|
||||
.append('{').append(label).append("=\"")
|
||||
.append(escapeLabelValue(labelValue))
|
||||
.append("\"} ")
|
||||
.append(value)
|
||||
.append('\n');
|
||||
}
|
||||
|
||||
private void appendHelpAndType(StringBuilder builder, String name, String help, String type) {
|
||||
builder.append("# HELP ").append(name).append(' ').append(help).append('\n');
|
||||
builder.append("# TYPE ").append(name).append(' ').append(type).append('\n');
|
||||
}
|
||||
|
||||
private void incrementLabeledCounter(ConcurrentHashMap<String, LongAdder> map, String labelValue) {
|
||||
String normalized = labelValue.toLowerCase(Locale.ROOT);
|
||||
map.computeIfAbsent(normalized, key -> new LongAdder()).increment();
|
||||
}
|
||||
|
||||
private String escapeLabelValue(String value) {
|
||||
return value.replace("\\", "\\\\")
|
||||
.replace("\n", "\\n")
|
||||
.replace("\"", "\\\"");
|
||||
}
|
||||
|
||||
private String formatDouble(double value) {
|
||||
return String.format(Locale.ROOT, "%.3f", value);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user