1
0
mirror of https://github.com/EssentialsX/Essentials.git synced 2025-04-07 04:26:18 +02:00

Update bStats Metrics and add wrapper class for new graphs ()

This PR updates the version of the bStats Metrics class to the latest version, supporting plugin IDs in place of just plugin names. It also adds the following graphs:
- Active permissions backend
- Active economy backend
- Whether or not a command has been used as a bar chart (pending bStats backend implementation)
- Version history graph as a multiline graph (also pending bStats impl)

It also removes the weird `getMetrics` and `setMetrics` APIs which should never have been API in the first place.
This commit is contained in:
MD 2020-07-05 19:30:01 +01:00 committed by GitHub
parent 8f86849aec
commit 1be3daf0b5
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 212 additions and 58 deletions
Essentials/src/com/earth2me/essentials
EssentialsAntiBuild/src/com/earth2me/essentials/antibuild
EssentialsChat/src/com/earth2me/essentials/chat
EssentialsGeoIP/src/com/earth2me/essentials/geoip
EssentialsProtect/src/com/earth2me/essentials/protect
EssentialsSpawn/src/com/earth2me/essentials/spawn
EssentialsXMPP/src/com/earth2me/essentials/xmpp

View File

@ -22,7 +22,7 @@ import com.earth2me.essentials.items.AbstractItemDb;
import com.earth2me.essentials.items.CustomItemResolver;
import com.earth2me.essentials.items.FlatItemDb;
import com.earth2me.essentials.items.LegacyItemDb;
import com.earth2me.essentials.metrics.Metrics;
import com.earth2me.essentials.metrics.MetricsWrapper;
import com.earth2me.essentials.perm.PermissionsHandler;
import com.earth2me.essentials.register.payment.Methods;
import com.earth2me.essentials.signs.SignBlockListener;
@ -103,7 +103,7 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
private transient UserMap userMap;
private transient ExecuteTimer execTimer;
private transient I18n i18n;
private transient Metrics metrics;
private transient MetricsWrapper metrics;
private transient EssentialsTimer timer;
private final transient Set<String> vanishedPlayers = new LinkedHashSet<>();
private transient Method oldGetOnlinePlayers;
@ -305,12 +305,7 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
for (World w : Bukkit.getWorlds())
addDefaultBackPermissionsToWorld(w);
metrics = new Metrics(this);
if (metrics.isEnabled()) {
getLogger().info("Starting Metrics. Opt-out using the global bStats config.");
} else {
getLogger().info("Metrics disabled per bStats config.");
}
metrics = new MetricsWrapper(this, 858, true);
final String timeroutput = execTimer.end();
if (getSettings().isDebug()) {
@ -518,6 +513,7 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
@Override
public boolean onCommand(final CommandSender sender, final Command command, final String commandLabel, final String[] args) {
metrics.markCommand(command.getName(), true);
return onCommandEssentials(sender, command, commandLabel, args, Essentials.class.getClassLoader(), "com.earth2me.essentials.commands.Command", "essentials.", null);
}
@ -686,16 +682,6 @@ public class Essentials extends JavaPlugin implements net.ess3.api.IEssentials {
return kits;
}
@Override
public Metrics getMetrics() {
return metrics;
}
@Override
public void setMetrics(Metrics metrics) {
this.metrics = metrics;
}
@Deprecated
@Override
public User getUser(final Object base) {

View File

@ -3,7 +3,6 @@ package com.earth2me.essentials;
import com.earth2me.essentials.api.IItemDb;
import com.earth2me.essentials.api.IJails;
import com.earth2me.essentials.api.IWarps;
import com.earth2me.essentials.metrics.Metrics;
import com.earth2me.essentials.perm.PermissionsHandler;
import com.earth2me.essentials.register.payment.Methods;
import net.ess3.provider.ServerStateProvider;
@ -93,10 +92,6 @@ public interface IEssentials extends Plugin {
UserMap getUserMap();
Metrics getMetrics();
void setMetrics(Metrics metrics);
EssentialsTimer getTimer();
@Deprecated

View File

@ -3,6 +3,7 @@ package com.earth2me.essentials.metrics;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.google.gson.JsonPrimitive;
import org.bukkit.Bukkit;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
@ -67,6 +68,9 @@ public class Metrics {
// The plugin
private final Plugin plugin;
// The plugin id
private final int pluginId;
// A list with all custom charts
private final List<CustomChart> charts = new ArrayList<>();
@ -74,12 +78,15 @@ public class Metrics {
* Class constructor.
*
* @param plugin The plugin which stats should be submitted.
* @param pluginId The id of the plugin.
* It can be found at <a href="https://bstats.org/what-is-my-plugin-id">What is my plugin id?</a>
*/
public Metrics(Plugin plugin) {
public Metrics(Plugin plugin, int pluginId) {
if (plugin == null) {
throw new IllegalArgumentException("Plugin cannot be null!");
}
this.plugin = plugin;
this.pluginId = pluginId;
// Get the config file
File bStatsFolder = new File(plugin.getDataFolder().getParentFile(), "bStats");
@ -194,6 +201,7 @@ public class Metrics {
String pluginVersion = plugin.getDescription().getVersion();
data.addProperty("pluginName", pluginName); // Append the name of the plugin
data.addProperty("id", pluginId); // Append the id of the plugin
data.addProperty("pluginVersion", pluginVersion); // Append the version of the plugin
JsonArray customCharts = new JsonArray();
for (CustomChart customChart : charts) {
@ -288,7 +296,6 @@ public class Metrics {
if (logFailedRequests) {
this.plugin.getLogger().log(Level.SEVERE, "Encountered unexpected exception", e);
}
continue; // continue looping since we cannot do any other thing.
}
}
} catch (NullPointerException | NoSuchMethodException | IllegalAccessException | InvocationTargetException ignored) { }
@ -327,7 +334,7 @@ public class Metrics {
throw new IllegalAccessException("This method must not be called from the main thread!");
}
if (logSentData) {
plugin.getLogger().info("Sending data to bStats: " + data.toString());
plugin.getLogger().info("Sending data to bStats: " + data);
}
HttpsURLConnection connection = (HttpsURLConnection) new URL(URL).openConnection();
@ -345,22 +352,20 @@ public class Metrics {
// Send data
connection.setDoOutput(true);
DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream());
outputStream.write(compressedData);
outputStream.flush();
outputStream.close();
InputStream inputStream = connection.getInputStream();
BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(inputStream));
try (DataOutputStream outputStream = new DataOutputStream(connection.getOutputStream())) {
outputStream.write(compressedData);
}
StringBuilder builder = new StringBuilder();
String line;
while ((line = bufferedReader.readLine()) != null) {
builder.append(line);
try (BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream()))) {
String line;
while ((line = bufferedReader.readLine()) != null) {
builder.append(line);
}
}
bufferedReader.close();
if (logResponseStatusText) {
plugin.getLogger().info("Sent data to bStats and received response: " + builder.toString());
plugin.getLogger().info("Sent data to bStats and received response: " + builder);
}
}
@ -376,9 +381,9 @@ public class Metrics {
return null;
}
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
GZIPOutputStream gzip = new GZIPOutputStream(outputStream);
gzip.write(str.getBytes(StandardCharsets.UTF_8));
gzip.close();
try (GZIPOutputStream gzip = new GZIPOutputStream(outputStream)) {
gzip.write(str.getBytes(StandardCharsets.UTF_8));
}
return outputStream.toByteArray();
}
@ -655,7 +660,7 @@ public class Metrics {
}
for (Map.Entry<String, Integer> entry : map.entrySet()) {
JsonArray categoryValues = new JsonArray();
categoryValues.add(entry.getValue());
categoryValues.add(new JsonPrimitive(entry.getValue()));
values.add(entry.getKey(), categoryValues);
}
data.add("values", values);
@ -699,7 +704,7 @@ public class Metrics {
allSkipped = false;
JsonArray categoryValues = new JsonArray();
for (int categoryValue : entry.getValue()) {
categoryValues.add(categoryValue);
categoryValues.add(new JsonPrimitive(categoryValue));
}
values.add(entry.getKey(), categoryValues);
}
@ -712,4 +717,4 @@ public class Metrics {
}
}
}
}

View File

@ -0,0 +1,137 @@
package com.earth2me.essentials.metrics;
import com.earth2me.essentials.Essentials;
import com.earth2me.essentials.register.payment.Methods;
import com.google.common.collect.ImmutableList;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class MetricsWrapper {
private final Essentials ess;
private final Metrics metrics;
private final Map<String, Boolean> commands = new HashMap<>();
private final Plugin plugin;
private static boolean hasWarned = false;
private static final List<String> KNOWN_FORCED_METRICS = ImmutableList.of("ChatControl");
public MetricsWrapper(Plugin plugin, int pluginId, boolean includeCommands) {
this.plugin = plugin;
this.ess = (Essentials) Bukkit.getPluginManager().getPlugin("Essentials");
this.metrics = new Metrics(plugin, pluginId);
if (metrics.isEnabled()) {
plugin.getLogger().info("Starting Metrics. Opt-out using the global bStats config.");
} else {
plugin.getLogger().info("Metrics disabled per bStats config.");
}
checkForcedMetrics();
addPermsChart();
addEconomyChart();
// bStats' backend currently doesn't support multi-line charts or advanced bar charts
// These are included for when bStats is ready to accept this data
addVersionHistoryChart();
if (includeCommands) addCommandsChart();
}
public void markCommand(String command, boolean state) {
commands.put(command, state);
}
public void addCustomChart(Metrics.CustomChart chart) {
metrics.addCustomChart(chart);
}
private void addPermsChart() {
metrics.addCustomChart(new Metrics.DrilldownPie("permsPlugin", () -> {
Map<String, Map<String, Integer>> result = new HashMap<>();
String handler = ess.getPermissionsHandler().getName();
Map<String, Integer> backend = new HashMap<>();
backend.put(ess.getPermissionsHandler().getBackendName(), 1);
result.put(handler, backend);
return result;
}));
}
private void addEconomyChart() {
metrics.addCustomChart(new Metrics.DrilldownPie("econPlugin", () -> {
Map<String, Map<String, Integer>> result = new HashMap<>();
Map<String, Integer> backend = new HashMap<>();
backend.put(Methods.getMethod().getPlugin().getName(), 1);
result.put(Methods.getMethod().getName(), backend);
return result;
}));
}
private void addVersionHistoryChart() {
metrics.addCustomChart(new Metrics.MultiLineChart("versionHistory", () -> {
HashMap<String, Integer> result = new HashMap<>();
result.put(plugin.getDescription().getVersion(), 1);
return result;
}));
}
private void addCommandsChart() {
for (String command : plugin.getDescription().getCommands().keySet()) {
markCommand(command, false);
}
metrics.addCustomChart(new Metrics.AdvancedBarChart("commands", () -> {
Map<String, int[]> result = new HashMap<>();
for (Map.Entry<String, Boolean> entry : commands.entrySet()) {
if (entry.getValue()) {
result.put(entry.getKey(), new int[]{1,0});
} else {
result.put(entry.getKey(), new int[]{0,1});
}
}
return result;
}));
}
private void checkForcedMetrics() {
if (hasWarned) return;
hasWarned = true;
Bukkit.getServer().getScheduler().scheduleSyncDelayedTask(plugin, () -> {
for (Class<?> service : Bukkit.getServicesManager().getKnownServices()) {
try {
service.getField("B_STATS_VERSION"); // Identifies bStats classes
if (KNOWN_FORCED_METRICS.contains(JavaPlugin.getProvidingPlugin(service).getName())) {
warnForcedMetrics(service);
} else {
try {
service.getDeclaredField("pluginId"); // Only present in recent bStats classes, which should also have the enabled field unless modified
} catch (NoSuchFieldException e) {
// Old bStats class found so "enabled" field detection is unreliable.
break;
}
try {
service.getDeclaredField("enabled"); // In modified forced metrics classes, this will fail
} catch (NoSuchFieldException e) {
warnForcedMetrics(service);
}
}
} catch (NoSuchFieldException ignored) {}
}
});
}
private void warnForcedMetrics(Class<?> service) {
Plugin servicePlugin = JavaPlugin.getProvidingPlugin(service);
plugin.getLogger().severe("WARNING: Potential forced metrics collection by plugin '" + servicePlugin.getName() + "' v" + servicePlugin.getDescription().getVersion());
plugin.getLogger().severe("Your server is running a plugin that may not respect bStats' opt-out settings.");
plugin.getLogger().severe("This may cause data to be uploaded to bStats.org for plugins that use bStats, even if you've opted out in the bStats config.");
plugin.getLogger().severe("Please report this to bStats and to the authors of '" + servicePlugin.getName() + "'. (Offending class: " + service.getName() + ")");
}
}

View File

@ -29,5 +29,7 @@ public interface IPermissionsHandler {
void unregisterContexts();
String getBackendName();
boolean tryProvider();
}

View File

@ -102,6 +102,11 @@ public class PermissionsHandler implements IPermissionsHandler {
handler.unregisterContexts();
}
@Override
public String getBackendName() {
return handler.getBackendName();
}
@Override
public boolean tryProvider() {
return true;

View File

@ -5,6 +5,7 @@ import net.milkbowl.vault.permission.Permission;
import org.bukkit.Bukkit;
import org.bukkit.entity.Player;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.java.JavaPlugin;
import java.util.Arrays;
import java.util.List;
@ -73,6 +74,11 @@ public abstract class AbstractVaultHandler extends SuperpermsHandler {
return null;
}
@Override
public String getBackendName() {
return JavaPlugin.getProvidingPlugin(perms.getClass()).getName();
}
boolean canLoad() {
if (Bukkit.getPluginManager().getPlugin("Vault") == null) return false;
try {

View File

@ -23,6 +23,11 @@ public class ConfigPermissionsHandler extends SuperpermsHandler {
return ess.getSettings().isPlayerCommand(cmds[cmds.length - 1]) || super.hasPermission(base, node);
}
@Override
public String getBackendName() {
return "Essentials";
}
@Override
public boolean tryProvider() {
return true;

View File

@ -82,6 +82,11 @@ public class SuperpermsHandler implements IPermissionsHandler {
public void unregisterContexts() {
}
@Override
public String getBackendName() {
return getEnabledPermsPlugin();
}
@Override
public boolean tryProvider() {
return getEnabledPermsPlugin() != null;

View File

@ -24,7 +24,7 @@ public interface Method {
* @see #getName()
* @see #getVersion()
*/
Object getPlugin();
Plugin getPlugin();
/**
* Returns the actual name of this method.

View File

@ -1,6 +1,7 @@
package com.earth2me.essentials.antibuild;
import com.earth2me.essentials.metrics.Metrics;
import com.earth2me.essentials.metrics.MetricsWrapper;
import org.bukkit.Material;
import org.bukkit.plugin.Plugin;
import org.bukkit.plugin.PluginManager;
@ -15,7 +16,7 @@ public class EssentialsAntiBuild extends JavaPlugin implements IAntiBuild {
private final transient Map<AntiBuildConfig, Boolean> settingsBoolean = new EnumMap<>(AntiBuildConfig.class);
private final transient Map<AntiBuildConfig, List<Material>> settingsList = new EnumMap<>(AntiBuildConfig.class);
private transient EssentialsConnect ess = null;
private transient Metrics metrics = null;
private transient MetricsWrapper metrics = null;
@Override
public void onEnable() {
@ -30,7 +31,7 @@ public class EssentialsAntiBuild extends JavaPlugin implements IAntiBuild {
pm.registerEvents(blockListener, this);
if (metrics == null) {
metrics = new Metrics(this);
metrics = new MetricsWrapper(this, 3813, false);
}
}

View File

@ -1,6 +1,7 @@
package com.earth2me.essentials.chat;
import com.earth2me.essentials.metrics.Metrics;
import com.earth2me.essentials.metrics.MetricsWrapper;
import net.ess3.api.IEssentials;
import org.bukkit.event.player.AsyncPlayerChatEvent;
import org.bukkit.plugin.PluginManager;
@ -16,7 +17,7 @@ import static com.earth2me.essentials.I18n.tl;
public class EssentialsChat extends JavaPlugin {
private transient Metrics metrics = null;
private transient MetricsWrapper metrics = null;
@Override
public void onEnable() {
@ -40,7 +41,7 @@ public class EssentialsChat extends JavaPlugin {
pluginManager.registerEvents(playerListenerHighest, this);
if (metrics == null) {
metrics = new Metrics(this);
metrics = new MetricsWrapper(this, 3814, false);
}
}

View File

@ -5,6 +5,8 @@ import static com.earth2me.essentials.I18n.tl;
import com.earth2me.essentials.metrics.Metrics;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.earth2me.essentials.metrics.MetricsWrapper;
import net.ess3.api.IEssentials;
import org.bukkit.plugin.PluginManager;
import org.bukkit.plugin.java.JavaPlugin;
@ -12,7 +14,7 @@ import org.bukkit.plugin.java.JavaPlugin;
public class EssentialsGeoIP extends JavaPlugin {
private transient Metrics metrics = null;
private transient MetricsWrapper metrics = null;
@Override
public void onEnable() {
@ -35,7 +37,7 @@ public class EssentialsGeoIP extends JavaPlugin {
getLogger().log(Level.INFO, "This product includes GeoLite2 data created by MaxMind, available from http://www.maxmind.com/.");
if (metrics == null) {
metrics = new Metrics(this);
metrics = new MetricsWrapper(this, 3815, false);
}
}

View File

@ -1,6 +1,7 @@
package com.earth2me.essentials.protect;
import com.earth2me.essentials.metrics.Metrics;
import com.earth2me.essentials.metrics.MetricsWrapper;
import com.earth2me.essentials.utils.VersionUtil;
import org.bukkit.Material;
import org.bukkit.entity.Player;
@ -20,7 +21,7 @@ public class EssentialsProtect extends JavaPlugin implements IProtect {
private final Map<ProtectConfig, String> settingsString = new EnumMap<>(ProtectConfig.class);
private final Map<ProtectConfig, List<Material>> settingsList = new EnumMap<>(ProtectConfig.class);
private EssentialsConnect ess = null;
private transient Metrics metrics = null;
private transient MetricsWrapper metrics = null;
private final EmergencyListener emListener = new EmergencyListener(this);
@ -36,7 +37,7 @@ public class EssentialsProtect extends JavaPlugin implements IProtect {
initialize(pm, essPlugin);
if (metrics == null) {
metrics = new Metrics(this);
metrics = new MetricsWrapper(this, 3816, false);
}
}

View File

@ -1,6 +1,6 @@
package com.earth2me.essentials.spawn;
import com.earth2me.essentials.metrics.Metrics;
import com.earth2me.essentials.metrics.MetricsWrapper;
import net.ess3.api.IEssentials;
import org.bukkit.Location;
import org.bukkit.command.Command;
@ -19,7 +19,7 @@ import static com.earth2me.essentials.I18n.tl;
public class EssentialsSpawn extends JavaPlugin implements IEssentialsSpawn {
private transient IEssentials ess;
private transient SpawnStorage spawns;
private transient Metrics metrics = null;
private transient MetricsWrapper metrics = null;
@Override
public void onEnable() {
@ -51,7 +51,7 @@ public class EssentialsSpawn extends JavaPlugin implements IEssentialsSpawn {
}
if (metrics == null) {
metrics = new Metrics(this);
metrics = new MetricsWrapper(this, 3817, true);
}
}
@ -61,6 +61,7 @@ public class EssentialsSpawn extends JavaPlugin implements IEssentialsSpawn {
@Override
public boolean onCommand(final CommandSender sender, final Command command, final String commandLabel, final String[] args) {
metrics.markCommand(command.getName(), true);
return ess.onCommandEssentials(sender, command, commandLabel, args, EssentialsSpawn.class.getClassLoader(), "com.earth2me.essentials.spawn.Command", "essentials.", spawns);
}

View File

@ -2,6 +2,7 @@ package com.earth2me.essentials.xmpp;
import com.earth2me.essentials.IEssentials;
import com.earth2me.essentials.metrics.Metrics;
import com.earth2me.essentials.metrics.MetricsWrapper;
import net.ess3.api.IUser;
import org.bukkit.command.Command;
import org.bukkit.command.CommandSender;
@ -21,7 +22,7 @@ public class EssentialsXMPP extends JavaPlugin implements IEssentialsXMPP {
private transient UserManager users;
private transient XMPPManager xmpp;
private transient IEssentials ess;
private transient Metrics metrics = null;
private transient MetricsWrapper metrics = null;
static IEssentialsXMPP getInstance() {
return instance;
@ -51,7 +52,7 @@ public class EssentialsXMPP extends JavaPlugin implements IEssentialsXMPP {
ess.addReloadListener(xmpp);
if (metrics == null) {
metrics = new Metrics(this);
metrics = new MetricsWrapper(this, 3818, true);
metrics.addCustomChart(new Metrics.SimplePie("config-valid", () -> xmpp.isConfigValid() ? "yes" : "no"));
}
}
@ -66,6 +67,7 @@ public class EssentialsXMPP extends JavaPlugin implements IEssentialsXMPP {
@Override
public boolean onCommand(final CommandSender sender, final Command command, final String commandLabel, final String[] args) {
metrics.markCommand(command.getName(), true);
return ess.onCommandEssentials(sender, command, commandLabel, args, EssentialsXMPP.class.getClassLoader(), "com.earth2me.essentials.xmpp.Command", "essentials.", null);
}