package openeye.logic;
import com.google.common.collect.HashMultiset;
import com.google.common.collect.Multiset;
import com.google.common.collect.Queues;
import java.lang.Thread.UncaughtExceptionHandler;
import java.util.Queue;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import openeye.Log;
import openeye.protocol.reports.ReportCrash;
import openeye.storage.IDataSource;
public class ThrowableLogger {
private static class ThrowableEntry {
public final String location;
public final Throwable throwable;
public ThrowableEntry(Throwable throwable, String location) {
this.location = location;
this.throwable = throwable;
}
}
private static Future<ModMetaCollector> resolver;
private static final Queue<ThrowableEntry> delayedThrowables = Queues.newConcurrentLinkedQueue();
private static final Multiset<String> locationCounters = HashMultiset.create();
private static void tryStoreCrash(Throwable throwable, String location) {
ModMetaCollector collector = null;
try {
collector = resolver != null? resolver.get(10, TimeUnit.SECONDS) : null;
} catch (Throwable t) {
Log.warn(t, "Failed to get resolver");
}
try {
ReportCrash crashReport = ReportBuilders.buildCrashReport(throwable, location, collector);
Storages storages = Storages.instance();
IDataSource<ReportCrash> crashStorage = storages.pendingCrashes.createNew();
crashStorage.store(crashReport);
} catch (Throwable t) {
Log.warn(t, "Failed to store crash report");
}
}
private static void storeAllPending() {
ThrowableEntry e;
while ((e = delayedThrowables.poll()) != null)
tryStoreCrash(e.throwable, e.location);
}
public static void init() {
Thread crashDumperThread = new Thread() {
@Override
public void run() {
storeAllPending();
}
};
crashDumperThread.setUncaughtExceptionHandler(new UncaughtExceptionHandler() {
@Override
public void uncaughtException(Thread t, Throwable e) {
System.err.println("[OpenEye] Exception in shutdown handler, report may not be sent");
e.printStackTrace();
}
});
Runtime.getRuntime().addShutdownHook(crashDumperThread);
}
public static void processThrowable(Throwable throwable, String location) {
if (throwable instanceof INotStoredCrash) return;
locationCounters.add(location);
if (locationCounters.count(location) > Config.storeCrashReportsLimit) {
Log.debug("Limit reached for location %s, skipping %s", location, throwable);
return;
}
if (resolver != null) tryStoreCrash(throwable, location);
else delayedThrowables.add(new ThrowableEntry(throwable, location));
}
public static void enableResolving(Future<ModMetaCollector> collector) {
resolver = collector;
storeAllPending();
}
}