All Downloads are FREE. Search and download functionalities are using the official Maven repository.

de.gurkenlabs.litiengine.DefaultUncaughtExceptionHandler Maven / Gradle / Ivy

The newest version!
package de.gurkenlabs.litiengine;

import static de.gurkenlabs.litiengine.util.io.FileUtilities.humanReadableByteCount;

import de.gurkenlabs.litiengine.configuration.ClientConfiguration;
import java.awt.DisplayMode;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.io.FileNotFoundException;
import java.io.PrintStream;
import java.lang.Thread.UncaughtExceptionHandler;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadInfo;
import java.lang.management.ThreadMXBean;
import java.time.Duration;
import java.util.Date;
import java.util.logging.Level;
import java.util.logging.Logger;

/**
 * Handles the uncaught exceptions that might occur while running a game or application with the
 * LITIENGINE.
 * 

* It provides proper logging of the exception in a {@code crash.txt} file in the game's root * directory that can be further used to report the issue if it's a generic one. *

*

* Depending on the configuration, the default behavior might force the game to exit upon an * unexpected exception which can be useful to detect problems in your game early. * * @see ClientConfiguration#exitOnError() */ public class DefaultUncaughtExceptionHandler implements UncaughtExceptionHandler { private static final Logger log = Logger.getLogger( DefaultUncaughtExceptionHandler.class.getName()); private volatile boolean exitOnException; private volatile boolean dumpThreads; /** * Initializes a new instance of the {@code DefaultUncaughtExceptionHandler} class. * * @param exitOnException A flag indicating whether the game should exit when an unexpected * exception occurs. The game will still exit if it encounters an Error. */ public DefaultUncaughtExceptionHandler(boolean exitOnException) { this(exitOnException, false); } /** * Initializes a new instance of the {@code DefaultUncaughtExceptionHandler} class. * * @param exitOnException A flag indicating whether the game should exit when an unexpected * exception occurs. The game will still exit if it encounters an Error * @param dumpThreads A flag indicating whether the crash report should contain an additional * thread dump. */ public DefaultUncaughtExceptionHandler(boolean exitOnException, boolean dumpThreads) { this.exitOnException = exitOnException; this.dumpThreads = dumpThreads; } @Override public void uncaughtException(final Thread t, final Throwable e) { try (PrintStream stream = new PrintStream("crash.txt")) { stream.print(new Date() + " "); stream.println(t.getName() + " threw an exception:"); e.printStackTrace(stream); stream.println("\n" + getSystemInfo()); if (dumpsThreads()) { stream.println(); stream.println(dump()); } } catch (FileNotFoundException e2) { log.log(Level.WARNING, "Could not create crash report file.", e); } log.log(Level.SEVERE, "Game crashed! :(", e); if (this.exitOnException() || e instanceof Error) { System.exit(Game.EXIT_GAME_CRASHED); } } /** * Indicates whether this hander currently exits the game upon an unhandled exception. *

* Note that this handler will still exit if it encounters an unhandled Error. * * @return True if the game will exit upon an unhandled exception; otherwise false. */ public boolean exitOnException() { return this.exitOnException; } /** * @return true if the generated crash report will contain a thread dump */ public boolean dumpsThreads() { return this.dumpThreads; } /** * Set whether the game will exit upon an unhandled exception. * * @param exit The flag that defines whether the game will exit upon an unhandled exception. */ public void setExitOnException(boolean exit) { this.exitOnException = exit; } /** * Set whether the generated crash report will contain an additional thread dump * * @param dumpThreads The flag that defines whether crash report will contain a thread dump. */ public void dumpThreads(boolean dumpThreads) { this.dumpThreads = dumpThreads; } protected static String dump() { StringBuilder text = new StringBuilder(); ThreadMXBean threads = ManagementFactory.getThreadMXBean(); ThreadInfo[] dumps = threads.getThreadInfo(threads.getAllThreadIds(), 255); text.append("====THREAD DUMP====\n\n"); for (ThreadInfo dump : dumps) { text.append("\"").append(dump.getThreadName()).append("\"\n"); Thread.State state = dump.getThreadState(); text.append("\tState: ").append(state); String blockedBy = dump.getLockOwnerName(); if (blockedBy != null) { text.append(" on ").append(blockedBy); } text.append("\n"); StackTraceElement[] elements = dump.getStackTrace(); for (StackTraceElement element : elements) { text.append("\t\tat "); text.append(element); text.append("\n"); } text.append("\n\n"); } return text.toString(); } protected static String getSystemInfo() { StringBuilder text = new StringBuilder(); text.append("====Runtime Information====\n"); text.append("Operating System: ").append(System.getProperty("os.name")).append("\n"); text.append("\tArchitecture: ").append(System.getProperty("os.arch")).append("\n"); text.append("\tVersion: ").append(System.getProperty("os.version")).append("\n"); text.append("Memory:\n"); long heapSize = Runtime.getRuntime().totalMemory(); long maxHeapSize = Runtime.getRuntime().maxMemory(); long freeHeapSize = Runtime.getRuntime().freeMemory(); text.append("\tMax heap size: ").append(humanReadableByteCount(maxHeapSize)).append("\n"); text.append("\tCurrent heap size: ").append(humanReadableByteCount(heapSize)).append("\n"); text.append("\tHeap used: ").append(humanReadableByteCount(heapSize - freeHeapSize)) .append("\n"); text.append("\tFree heap: ").append(humanReadableByteCount(freeHeapSize)).append("\n"); text.append("Java Version: ").append(System.getProperty("java.runtime.name")).append(" ") .append(System.getProperty("java.runtime.version")) .append(" \n"); text.append("\tVendor: ").append(System.getProperty("java.vm.vendor")).append("\n"); text.append("Uptime: ") .append(Duration.ofMillis(ManagementFactory.getRuntimeMXBean().getUptime())).append("\n"); GraphicsEnvironment g = GraphicsEnvironment.getLocalGraphicsEnvironment(); GraphicsDevice[] screens = g.getScreenDevices(); text.append("Screens: ").append(screens.length).append("\n"); for (int i = 0; i < screens.length; i++) { GraphicsDevice screen = screens[i]; DisplayMode displayMode = screen.getDisplayMode(); text.append("\tScreen ").append(i).append(": ").append(displayMode.getWidth()).append("x") .append(displayMode.getHeight()); if (displayMode.getRefreshRate() != DisplayMode.REFRESH_RATE_UNKNOWN) { text.append("@").append(displayMode.getRefreshRate()).append("hz"); } text.append("\n"); } return text.toString(); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy