Skip to content

EndlessCodeGroup/Inspector

Repository files navigation

Inspector

Version License

Inspector Example

Inspector helps developers to track all exceptions and crashes of theirs plugins. It automatically sends reports to a developer with all needed information about an environment.

It sends not sensitive data:

  • Plugin name and version
  • Version of Inspector
  • Exception stacktrace
  • Unique ID of server (it can't be used to determine who sent a report, it used only to determine that "two reports sent from same server")

Also, it sends some sensitive data that can be disabled from sending:

  • Server core and version
  • List of plugins with versions

NOTE: Inspector filters all tracked exceptions from console to not bother server owners. All plugin-related logs will be saved to log file in the plugin folder.

Navigation

For server owners

This is not a plugin and can't be installed with copying to plugins directory.

You can disable sending of information about server core and installed plugins in the inspector.yml that stored in a directory of each plugin that uses Inspector. Also, you can configure it globally in plugins/Inspector/config.yml.

Config example

Reporter:
  enabled: true
  # Here you can choose what you want to send
  data:
    core: true    # Info about server core
    plugins: true # Plugins list

For plugin developers

To add Inspector to the plugin you must:

  • Add Inspector as a dependency to the project
  • Modify main plugin class
  • Change main class in plugin.yml to new

Also, for more coverage, you should:

  • Change all usages of BukkitRunnable to TrackedBukkitRunnable
  • Wrap CommandExecutor with TrackedCommandExecutor

Add Inspector to the project

plugins {
    // Add shadow plugin to shade inspector
    // See: http://imperceptiblethoughts.com/shadow/
    id 'com.github.johnrengelman.shadow' version '7.0.0'
}

// Inspector is published at Maven Central
repositories {
    mavenCentral()
}

shadowJar {
    // Exclude dependencies bundled into Spigot from resulting JAR
    dependencies {
        exclude(dependency("com.google.code.gson:gson:.*"))
        exclude(dependency("org.jetbrains:annotations:.*"))
    }

    // Remove some extra files from resulting JAR
    exclude("DebugProbesKt.bin")
    exclude("META-INF/proguard/**") // If you don't use proguard
    exclude("META-INF/native-image/**")

    // To avoid possible conflicts we should relocate embedded dependencies to own unique package
    // Here we use manual relocating, but easiest (and slower) variant is use automatically relocating.
    // Read more: https://imperceptiblethoughts.com/shadow/configuration/relocation/#automatically-relocating-dependencies
    def shadowPackage = "shadow.[PLACE_HERE_YOUR_PLUGIN_PACKAGE]"
    relocate "ru.endlesscode.inspector", "${shadowPackage}.inspector"
    relocate "kotlinx", "${shadowPackage}.kotlinx"
    relocate "kotlin", "${shadowPackage}.kotlin"

    // If you use inspector-sentry-reporter:
    relocate "io.sentry", "${shadowPackage}.sentry"

    // If you use inspector-discord-reporter:
    relocate "com.github.kittinunf", "${shadowPackage}.kittinunf"

    // Enable shadowJar minimization to reduce plugin size.
    // Read more: https://imperceptiblethoughts.com/shadow/configuration/minimizing/
    minimize()
}

// Automatically run shadowJar making on assemble
tasks.assemble.dependsOn tasks.shadowJar

// Here you can change preferred version of inspector
ext.inspectorVerson = "0.12.1"

// Add Inspector as dependency
// 'inspector-bukkit' - implementation of Inspector for Bukkit.
// 'inspector-sentry-reporter' - reporter that we want to use (read above about available reporters)
dependencies {
    implementation "ru.endlesscode.inspector:inspector-bukkit:$inspectorVerson"
    implementation "ru.endlesscode.inspector:inspector-sentry-reporter:$inspectorVerson"
    implementation "ru.endlesscode.inspector:sentry-bukkit:$inspectorVerson" // If you want SentryBukkitIntegration
}

Main plugin class modifications

First, your current main plugin class should extend PluginLifecycle instead of JavaPlugin.
For example, this code:

public class MyPlugin extends JavaPlugin {
    // ...
    // onEnable, onDisable, etc.
    // ...
}

must become:

public class MyPlugin extends PluginLifecycle {
    // ...
    // onEnable, onDisable, etc.
    // ... 
}

If you're doing anything that requires access to plugin's methods in a constructor, you will get UninitializedPropertyAccessException. To avoid this problem, override method init() and do the work within:

public class MyPlugin extends PluginLifecycle {
    @Override
    public void init() {
        // do some work, using plugin's methods
    }
}

Next, you must create the new class extending TrackedPlugin that will be used as main plugin class and link it with the lifecycle. Also, you must override method createReporter(). The created reporter will be used for reporting errors.
Example:

public class MyTrackedPlugin extends TrackedPlugin {

    public MyTrackedPlugin() {
        super(MyPlugin.class); // Pass here lifecycle class
    }

    @Override
    public Reporter createReporter() {
        String dsn = "[YOUR_DSN_HERE]";

        // Note that you should add needed reporter as dependency first.
        return new SentryReporter.Builder()
                .setDsn(dsn)
                // If you want more detailed reports, add this, but you also should
                // add `sentry-bukkit` dependency before
                .addIntegration(new SentryBukkitIntegration(this))
                .focusOn(this) // Reporter will be focused on this plugin
                .build();
    }
}