Skip to content

Instantly share code, notes, and snippets.

@Xyene
Created October 14, 2012 18:44
Show Gist options
  • Save Xyene/3889436 to your computer and use it in GitHub Desktop.
Save Xyene/3889436 to your computer and use it in GitHub Desktop.
Multiple-Plugin Capable ErrorLogger
/**
* Custom logger to save errors.
*
* @author Icyene
*/
public class ErrorLogger extends PluginLogger {
public static ArrayList<Pair<String, ArrayList<String>>> registry = new ArrayList<Pair<String, ArrayList<String>>>();
public static boolean inited = false;
private static Field logger_mc, cb_mcs, logger;
public ErrorLogger(Plugin context) {
super(context);
register(context, "NULL", "NULL", "NULL");
getFields();
}
public ErrorLogger(final Plugin context, final String name, final String pack, final String tracker) {
super(context);
register(context, name, pack, tracker);
getFields();
}
@Override
public void log(LogRecord logRecord) {
if (!generateErrorLog(logRecord))
super.log(logRecord);
}
private static void getFields() {
try {
logger_mc = MinecraftServer.class.getDeclaredField("log");
logger_mc.setAccessible(true);
cb_mcs = CraftServer.class.getDeclaredField("console");
cb_mcs.setAccessible(true);
logger = JavaPlugin.class.getDeclaredField("logger");
logger.setAccessible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
private void register(final String name, final String pack, final String tracker, final String endl) {
//This line is horrendously hard to read. It works, though.
registry.add(
new Pair<String, ArrayList<String>>(name,
new ArrayList<String>() {
{
add(pack);
add(tracker);
add(endl);
}
}
)
);
}
private static String getEndline(String name) {
String endl = "";
StringBuilder temp = new StringBuilder();
for (int i = 0; i < name.length(); ++i)
temp.append('=');
endl = endl + temp.toString();
return endl;
}
public void register(Plugin context, String name, String pack, String tracker) {
try {
ErrorLogger log = new ErrorLogger(context, name, pack, tracker);
if (!ErrorLogger.inited) //If Thread.setDefaultUncaughtExceptionHandler hasn't been used
initErrorHandler(); //Use it
if (!(logger.get(context) instanceof ErrorLogger))
logger.set(context, log);
if (!(logger_mc.get(cb_mcs) instanceof ErrorLogger))
logger_mc.set(cb_mcs, log);
register(name, pack, tracker, getEndline(name));
} catch (Exception e) {
e.printStackTrace();
}
}
private static boolean generateErrorLog(LogRecord record) {
Server server = Bukkit.getServer();
Throwable thrown;
if ((thrown = record.getThrown()) == null)
return false;
String ERROR = ExceptionUtils.getStackTrace(thrown), NAME = "", TICKETS = "", ENDL = "";
Plugin PLUGIN = null;
for (Pair par : registry) {
ArrayList<String> data = (ArrayList) par.RIGHT;
if (ERROR.contains(data.get(0))) { //If the ERROR contains the package
NAME = (String) par.LEFT;
PLUGIN = Bukkit.getPluginManager().getPlugin(NAME);
TICKETS = data.get(1);
ENDL = data.get(2);
break;
}
}
if (!ERROR.contains(NAME + " has encountered an error!") && !ERROR.contains(this.getClass().getName())) //Check if its not our own
return false;
PluginDescriptionFile pdf = PLUGIN.getDescription();
StringBuilder err = new StringBuilder();
boolean disable = false;
err.append("\n=============" + NAME + " has encountered an error!=============");
err.append("\nStacktrace:\n" + ERROR);
err.append("\n" + NAME + " version: " + pdf.getVersion());
err.append("\nBukkit message: " + record.getMessage());
err.append("\nPlugins loaded: " + Arrays.asList(server.getPluginManager().getPlugins()));
err.append("\nCraftBukkit version: " + server.getBukkitVersion());
err.append("\nJava version: " + getProperty("java.version"));
err.append("\nOS info: " + getProperty("os.arch") + " " + getProperty("os.name") + ", " + getProperty("os.version"));
err.append("\nPlease report this error to the " + NAME + " ticket tracker (" + TICKETS + ")!");
ERROR = ERROR.toLowerCase();
if (ERROR.contains("nullpointerexception") || ERROR.contains("stackoverflowexception")) {
err.append("\nA critical error has been thrown. " + NAME + " has been disabled to prevent further damage.");
disable = true;
} else {
err.append("\nError was minor; " + NAME + " will continue operating.");
}
String name = NAME + "_" + md5(err).substring(0, 6) + ".error.log"; //Name is PLUGIN_NAME with first 6 chars of md5 appended
File root = new File(PLUGIN.getDataFolder(), "errors");
if (!root.exists())
root.mkdir();
File dump = new File(root.getAbsoluteFile(), name);
if (!dump.exists()) {
try {
dump.createNewFile();
BufferedWriter writer = new BufferedWriter(new FileWriter(dump));
writer.write((err.toString() + ENDL).substring(1)); //Remove the extra /n
writer.close();
err.append("\nThis has been saved to the file ./" + PLUGIN.getName() + "/errors/" + name);
} catch (Exception e) {
System.err.println("Ehm, errors occured while displaying an error >.< Stacktrace:\n");
e.printStackTrace();
}
}
err.append(ENDL);
System.err.println(err);
if (disable)
Bukkit.getServer().getPluginManager().disablePlugin(PLUGIN);
return true;
}
private static void initErrorHandler() {
Thread.setDefaultUncaughtExceptionHandler(new Thread.UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread thread, Throwable throwable) {
LogRecord o_u_screwd = new LogRecord(Level.SEVERE, "Uhoh");
o_u_screwd.setThrown(throwable);
generateErrorLog(o_u_screwd);
}
});
ErrorLogger.inited = true;
}
private static String md5(StringBuilder builder) {
String hash = "";
try {
MessageDigest m = MessageDigest.getInstance("MD5");
m.reset();
m.update(builder.toString().getBytes());
hash = new BigInteger(1, m.digest()).toString(16);
while (hash.length() < 32) {
hash = 0 + hash;
}
} catch (NoSuchAlgorithmException e) {
}
return hash;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment