From 329e136a66760b369011f86006bf1f13f37aa13b Mon Sep 17 00:00:00 2001 From: Julian Krings <47589149+CrazyDev05@users.noreply.github.com> Date: Fri, 6 Jun 2025 17:33:16 +0200 Subject: [PATCH] implement opt-out auto reporting with sentry (#1201) --- build.gradle | 10 ++++ core/src/main/java/com/volmit/iris/Iris.java | 40 +++++++++++++++- .../com/volmit/iris/core/IrisSettings.java | 7 +++ .../volmit/iris/core/link/WorldEditLink.java | 1 + .../com/volmit/iris/core/loader/IrisData.java | 1 + .../iris/core/pregenerator/ChunkUpdater.java | 2 + .../pregenerator/cache/PregenCacheImpl.java | 2 + .../methods/AsyncPregenMethod.java | 8 +++- .../iris/core/safeguard/IrisSafeguard.java | 10 ++++ .../volmit/iris/util/scheduling/Looper.java | 1 - .../volmit/iris/util/sentry/Attachments.java | 41 ++++++++++++++++ .../volmit/iris/util/sentry/IrisLogger.java | 47 +++++++++++++++++++ 12 files changed, 166 insertions(+), 4 deletions(-) create mode 100644 core/src/main/java/com/volmit/iris/util/sentry/Attachments.java create mode 100644 core/src/main/java/com/volmit/iris/util/sentry/IrisLogger.java diff --git a/build.gradle b/build.gradle index 88f7a3b94..45f2d09fd 100644 --- a/build.gradle +++ b/build.gradle @@ -33,6 +33,7 @@ plugins { id "io.github.goooler.shadow" version "8.1.7" id "de.undercouch.download" version "5.0.1" id "xyz.jpenilla.run-paper" version "2.3.1" + id "io.sentry.jvm.gradle" version "5.7.0" } @@ -132,6 +133,7 @@ configurations.configureEach { allprojects { apply plugin: 'java' + apply plugin: 'io.sentry.jvm.gradle' repositories { mavenCentral() @@ -203,6 +205,14 @@ allprojects { archiveClassifier.set('javadoc') from javadoc.destinationDir } + + sentry { + includeSourceContext = true + + org = "volmit-software" + projectName = "iris" + authToken = property("sentry.auth.token") + } } if (JavaVersion.current().toString() != "21") { diff --git a/core/src/main/java/com/volmit/iris/Iris.java b/core/src/main/java/com/volmit/iris/Iris.java index 5bf32b68f..34dd8ab68 100644 --- a/core/src/main/java/com/volmit/iris/Iris.java +++ b/core/src/main/java/com/volmit/iris/Iris.java @@ -32,7 +32,6 @@ import com.volmit.iris.core.nms.v1X.NMSBinding1X; import com.volmit.iris.core.pregenerator.LazyPregenerator; import com.volmit.iris.core.service.StudioSVC; import com.volmit.iris.core.tools.IrisToolbelt; -import com.volmit.iris.core.tools.IrisWorldCreator; import com.volmit.iris.engine.EnginePanic; import com.volmit.iris.engine.object.IrisCompat; import com.volmit.iris.engine.object.IrisContextInjector; @@ -64,7 +63,10 @@ import com.volmit.iris.util.reflect.ShadeFix; import com.volmit.iris.util.scheduling.J; import com.volmit.iris.util.scheduling.Queue; import com.volmit.iris.util.scheduling.ShurikenQueue; +import com.volmit.iris.util.sentry.Attachments; +import com.volmit.iris.util.sentry.IrisLogger; import io.papermc.lib.PaperLib; +import io.sentry.Sentry; import net.kyori.adventure.platform.bukkit.BukkitAudiences; import net.kyori.adventure.text.serializer.ComponentSerializer; import org.bstats.bukkit.Metrics; @@ -392,6 +394,7 @@ public class Iris extends VolmitPlugin implements Listener { } public static void reportError(Throwable e) { + Sentry.captureException(e); if (IrisSettings.get().getGeneral().isDebug()) { String n = e.getClass().getCanonicalName() + "-" + e.getStackTrace()[0].getClassName() + "-" + e.getStackTrace()[0].getLineNumber(); @@ -456,6 +459,7 @@ public class Iris extends VolmitPlugin implements Listener { instance = this; services = new KMap<>(); setupAudience(); + setupSentry(); initialize("com.volmit.iris.core.service").forEach((i) -> services.put((Class) i.getClass(), (IrisService) i)); INMS.get(); IO.delete(new File("iris")); @@ -486,6 +490,12 @@ public class Iris extends VolmitPlugin implements Listener { checkForBukkitWorlds(); IrisToolbelt.retainMantleDataForSlice(String.class.getCanonicalName()); IrisToolbelt.retainMantleDataForSlice(BlockData.class.getCanonicalName()); + + try { + throw new Exception("This is a test exception"); + } catch (Exception e) { + reportError(e); + } }); } @@ -527,6 +537,7 @@ public class Iris extends VolmitPlugin implements Listener { } } catch (Throwable e) { e.printStackTrace(); + Sentry.captureException(e); } } @@ -544,7 +555,7 @@ public class Iris extends VolmitPlugin implements Listener { }); }); } catch (IrisException e) { - e.printStackTrace(); + Sentry.captureException(e); } } } @@ -940,4 +951,29 @@ public class Iris extends VolmitPlugin implements Listener { return -1; } } + + private static void setupSentry() { + var settings = IrisSettings.get().getSentry(); + if (settings.disableAutoReporting || Sentry.isEnabled()) return; + Sentry.init(options -> { + options.setDsn("https://9e190553a1aea18f1cab566cbc894d3f@o4509451052646400.ingest.de.sentry.io/4509451115036752"); + if (settings.debug) { + options.setLogger(new IrisLogger()); + options.setDebug(true); + } + + options.setBeforeSend((event, hint) -> { + event.setTag("iris.safeguard", IrisSafeguard.mode()); + event.setTag("iris.nms", INMS.get().getClass().getCanonicalName()); + return event; + }); + }); + Sentry.configureScope(scope -> { + scope.addAttachment(Attachments.PLUGINS); + scope.setTag("server", Bukkit.getVersion()); + scope.setTag("server.type", Bukkit.getName()); + scope.setTag("server.api", Bukkit.getBukkitVersion()); + }); + Runtime.getRuntime().addShutdownHook(new Thread(Sentry::close)); + } } diff --git a/core/src/main/java/com/volmit/iris/core/IrisSettings.java b/core/src/main/java/com/volmit/iris/core/IrisSettings.java index b7e5bae20..20c0fa265 100644 --- a/core/src/main/java/com/volmit/iris/core/IrisSettings.java +++ b/core/src/main/java/com/volmit/iris/core/IrisSettings.java @@ -46,6 +46,7 @@ public class IrisSettings { private IrisSettingsPerformance performance = new IrisSettingsPerformance(); private IrisSettingsUpdater updater = new IrisSettingsUpdater(); private IrisSettingsPregen pregen = new IrisSettingsPregen(); + private IrisSettingsSentry sentry = new IrisSettingsSentry(); public static int getThreadCount(int c) { return switch (c) { @@ -222,6 +223,12 @@ public class IrisSettings { } } + @Data + public static class IrisSettingsSentry { + public boolean disableAutoReporting = false; + public boolean debug = false; + } + @Data public static class IrisSettingsGUI { public boolean useServerLaunchedGuis = true; diff --git a/core/src/main/java/com/volmit/iris/core/link/WorldEditLink.java b/core/src/main/java/com/volmit/iris/core/link/WorldEditLink.java index 42e335a10..638c66cc2 100644 --- a/core/src/main/java/com/volmit/iris/core/link/WorldEditLink.java +++ b/core/src/main/java/com/volmit/iris/core/link/WorldEditLink.java @@ -47,6 +47,7 @@ public class WorldEditLink { } catch (Throwable e) { Iris.error("Could not get selection"); e.printStackTrace(); + Iris.reportError(e); active.reset(); active.aquire(() -> false); } diff --git a/core/src/main/java/com/volmit/iris/core/loader/IrisData.java b/core/src/main/java/com/volmit/iris/core/loader/IrisData.java index e51c93176..a272be0d7 100644 --- a/core/src/main/java/com/volmit/iris/core/loader/IrisData.java +++ b/core/src/main/java/com/volmit/iris/core/loader/IrisData.java @@ -300,6 +300,7 @@ public class IrisData implements ExclusionStrategy, TypeAdapterFactory { return r; } catch (Throwable e) { + Iris.reportError(e); e.printStackTrace(); Iris.error("Failed to create loader! " + registrant.getCanonicalName()); } diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/ChunkUpdater.java b/core/src/main/java/com/volmit/iris/core/pregenerator/ChunkUpdater.java index 9f9bde99e..572ce98d4 100644 --- a/core/src/main/java/com/volmit/iris/core/pregenerator/ChunkUpdater.java +++ b/core/src/main/java/com/volmit/iris/core/pregenerator/ChunkUpdater.java @@ -109,6 +109,7 @@ public class ChunkUpdater { } } } catch (Exception e) { + Iris.reportError(e); e.printStackTrace(); } }, 0, 3, TimeUnit.SECONDS); @@ -314,6 +315,7 @@ public class ChunkUpdater { world.save(); }).get(); } catch (Throwable e) { + Iris.reportError(e); e.printStackTrace(); } } diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/cache/PregenCacheImpl.java b/core/src/main/java/com/volmit/iris/core/pregenerator/cache/PregenCacheImpl.java index c3404eda5..b9a3de943 100644 --- a/core/src/main/java/com/volmit/iris/core/pregenerator/cache/PregenCacheImpl.java +++ b/core/src/main/java/com/volmit/iris/core/pregenerator/cache/PregenCacheImpl.java @@ -71,6 +71,7 @@ class PregenCacheImpl implements PregenCache { return new Plate(key, in); } catch (IOException e){ Iris.error("Failed to read pregen cache " + file); + Iris.reportError(e); e.printStackTrace(); return new Plate(key); } @@ -87,6 +88,7 @@ class PregenCacheImpl implements PregenCache { IO.write(file, out -> new DataOutputStream(new LZ4BlockOutputStream(out)), plate::write); } catch (IOException e) { Iris.error("Failed to write pregen cache " + file); + Iris.reportError(e); e.printStackTrace(); } } finally { diff --git a/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java b/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java index 757dfcf8f..0dcfffcfc 100644 --- a/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java +++ b/core/src/main/java/com/volmit/iris/core/pregenerator/methods/AsyncPregenMethod.java @@ -157,7 +157,10 @@ public class AsyncPregenMethod implements PregeneratorMethod { } catch (Throwable e) { Iris.warn("Failed to increase worker threads, if you are on paper or a fork of it please increase it manually to " + adjusted); Iris.warn("For more information see https://docs.papermc.io/paper/reference/global-configuration#chunk_system_worker_threads"); - if (e instanceof InvocationTargetException) e.printStackTrace(); + if (e instanceof InvocationTargetException) { + Iris.reportError(e); + e.printStackTrace(); + } } return 0; }); @@ -173,6 +176,7 @@ public class AsyncPregenMethod implements PregeneratorMethod { method.invoke(pool, i); return 0; } catch (Throwable e) { + Iris.reportError(e); Iris.error("Failed to reset worker threads"); e.printStackTrace(); } @@ -201,6 +205,7 @@ public class AsyncPregenMethod implements PregeneratorMethod { }).get(); } catch (InterruptedException ignored) { } catch (Throwable e) { + Iris.reportError(e); e.printStackTrace(); } finally { semaphore.release(); @@ -219,6 +224,7 @@ public class AsyncPregenMethod implements PregeneratorMethod { public void generate(int x, int z, PregenListener listener) { PaperLib.getChunkAtAsync(world, x, z, true, urgent) .exceptionally(e -> { + Iris.reportError(e); e.printStackTrace(); return null; }) diff --git a/core/src/main/java/com/volmit/iris/core/safeguard/IrisSafeguard.java b/core/src/main/java/com/volmit/iris/core/safeguard/IrisSafeguard.java index 5217be9f2..2c9eb9be1 100644 --- a/core/src/main/java/com/volmit/iris/core/safeguard/IrisSafeguard.java +++ b/core/src/main/java/com/volmit/iris/core/safeguard/IrisSafeguard.java @@ -20,5 +20,15 @@ public class IrisSafeguard { Iris.instance.splash(); UtilsSFG.splash(); } + + public static String mode() { + if (unstablemode) { + return "unstable"; + } else if (warningmode) { + return "warning"; + } else { + return "stable"; + } + } } diff --git a/core/src/main/java/com/volmit/iris/util/scheduling/Looper.java b/core/src/main/java/com/volmit/iris/util/scheduling/Looper.java index e5be20b84..72ba9ff03 100644 --- a/core/src/main/java/com/volmit/iris/util/scheduling/Looper.java +++ b/core/src/main/java/com/volmit/iris/util/scheduling/Looper.java @@ -36,7 +36,6 @@ public abstract class Looper extends Thread { //noinspection BusyWait Thread.sleep(m); } catch (InterruptedException e) { - Iris.reportError(e); break; } catch (Throwable e) { Iris.reportError(e); diff --git a/core/src/main/java/com/volmit/iris/util/sentry/Attachments.java b/core/src/main/java/com/volmit/iris/util/sentry/Attachments.java new file mode 100644 index 000000000..a338ea6ce --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/sentry/Attachments.java @@ -0,0 +1,41 @@ +package com.volmit.iris.util.sentry; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.volmit.iris.util.collection.KMap; +import io.sentry.Attachment; +import org.bukkit.Bukkit; + +import java.nio.charset.StandardCharsets; +import java.util.concurrent.Callable; + +public class Attachments { + private static final Gson GSON = new GsonBuilder().disableHtmlEscaping().create(); + public static final Attachment PLUGINS = jsonProvider(Attachments::plugins, "plugins.json"); + + public static Attachment json(Object object, String name) { + return new Attachment(GSON.toJson(object).getBytes(StandardCharsets.UTF_8), name, "application/json", "event.attachment", true); + } + + public static Attachment jsonProvider(Callable object, String name) { + return new Attachment(() -> GSON.toJson(object.call()).getBytes(StandardCharsets.UTF_8), name, "application/json", "event.attachment", true); + } + + private static KMap plugins() { + KMap enabled = new KMap<>(); + KMap disabled = new KMap<>(); + + var pm = Bukkit.getPluginManager(); + for (var plugin : pm.getPlugins()) { + if (plugin.isEnabled()) { + enabled.put(plugin.getName(), plugin.getDescription().getVersion()); + } else { + disabled.put(plugin.getName(), plugin.getDescription().getVersion()); + } + } + + return new KMap() + .qput("enabled", enabled) + .qput("disabled", disabled); + } +} diff --git a/core/src/main/java/com/volmit/iris/util/sentry/IrisLogger.java b/core/src/main/java/com/volmit/iris/util/sentry/IrisLogger.java new file mode 100644 index 000000000..e51d407c5 --- /dev/null +++ b/core/src/main/java/com/volmit/iris/util/sentry/IrisLogger.java @@ -0,0 +1,47 @@ +package com.volmit.iris.util.sentry; + +import com.volmit.iris.Iris; +import io.sentry.ILogger; +import io.sentry.SentryLevel; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.io.PrintWriter; +import java.io.StringWriter; + +public class IrisLogger implements ILogger { + @Override + public void log(@NotNull SentryLevel level, @NotNull String message, @Nullable Object... args) { + Iris.msg(String.format("%s: %s", level, String.format(message, args))); + } + + @Override + public void log(@NotNull SentryLevel level, @NotNull String message, @Nullable Throwable throwable) { + if (throwable == null) { + log(level, message); + } else { + Iris.msg(String.format("%s: %s\n%s", level, String.format(message, throwable), captureStackTrace(throwable))); + } + } + + @Override + public void log(@NotNull SentryLevel level, @Nullable Throwable throwable, @NotNull String message, @Nullable Object... args) { + if (throwable == null) { + log(level, message, args); + } else { + Iris.msg(String.format("%s: %s\n%s", level, String.format(message, throwable), captureStackTrace(throwable))); + } + } + + @Override + public boolean isEnabled(@Nullable SentryLevel level) { + return true; + } + + private @NotNull String captureStackTrace(@NotNull Throwable throwable) { + StringWriter stringWriter = new StringWriter(); + PrintWriter printWriter = new PrintWriter(stringWriter); + throwable.printStackTrace(printWriter); + return stringWriter.toString(); + } +}