Funkin/source/funkin/util/logging/CrashHandler.hx

171 lines
4.3 KiB
Haxe
Raw Normal View History

package funkin.util.logging;
import openfl.Lib;
import openfl.events.UncaughtErrorEvent;
/**
* A custom crash handler that writes to a log file and displays a message box.
*/
@:nullSafety
class CrashHandler
{
static final LOG_FOLDER = 'logs';
/**
* Initializes
*/
public static function initialize():Void
{
trace('[LOG] Enabling standard uncaught error handler...');
Lib.current.loaderInfo.uncaughtErrorEvents.addEventListener(UncaughtErrorEvent.UNCAUGHT_ERROR, onUncaughtError);
#if cpp
trace('[LOG] Enabling C++ critical error handler...');
untyped __global__.__hxcpp_set_critical_error_handler(onCriticalError);
#end
}
/**
* Called when an uncaught error occurs.
* This handles most thrown errors, and is sufficient to handle everything alone on HTML5.
* @param error Information on the error that was thrown.
*/
static function onUncaughtError(error:UncaughtErrorEvent):Void
{
try
{
#if sys
logError(error);
#end
displayError(error);
}
catch (e:Dynamic)
{
trace('Error while handling crash: ' + e);
}
}
static function onCriticalError(message:String):Void
{
try
{
#if sys
logErrorMessage(message, true);
#end
displayErrorMessage(message);
}
catch (e:Dynamic)
{
trace('Error while handling crash: $e');
trace('Message: $message');
}
}
static function displayError(error:UncaughtErrorEvent):Void
{
displayErrorMessage(generateErrorMessage(error));
}
static function displayErrorMessage(message:String):Void
{
lime.app.Application.current.window.alert(message, "Fatal Uncaught Exception");
}
#if sys
static function logError(error:UncaughtErrorEvent):Void
{
logErrorMessage(generateErrorMessage(error));
}
static function logErrorMessage(message:String, critical:Bool = false):Void
{
FileUtil.createDirIfNotExists(LOG_FOLDER);
sys.io.File.saveContent('$LOG_FOLDER/crash${critical ? '-critical' : ''}-${DateUtil.generateTimestamp()}.log', buildCrashReport(message));
}
static function buildCrashReport(message:String):String
{
var fullContents:String = '=====================\n';
fullContents += ' Funkin Crash Report\n';
fullContents += '=====================\n';
fullContents += '\n';
fullContents += 'Generated by: ${Constants.GENERATED_BY}\n';
fullContents += 'System timestamp: ${DateUtil.generateTimestamp()}\n';
var driverInfo = FlxG?.stage?.context3D?.driverInfo ?? 'N/A';
fullContents += 'Driver info: ${driverInfo}\n';
fullContents += 'Platform: ${Sys.systemName()}\n';
fullContents += '\n';
fullContents += '=====================\n';
fullContents += '\n';
fullContents += message;
fullContents += '\n';
return fullContents;
}
#end
static function generateErrorMessage(error:UncaughtErrorEvent):String
{
var errorMessage:String = "";
var callStack:Array<haxe.CallStack.StackItem> = haxe.CallStack.exceptionStack(true);
errorMessage += '${error.error}\n';
for (stackItem in callStack)
{
switch (stackItem)
{
case FilePos(innerStackItem, file, line, column):
errorMessage += ' in ${file}#${line}';
if (column != null) errorMessage += ':${column}';
case CFunction:
errorMessage += '[Function] ';
case Module(m):
errorMessage += '[Module(${m})] ';
case Method(classname, method):
errorMessage += '[Function(${classname}.${method})] ';
case LocalFunction(v):
errorMessage += '[LocalFunction(${v})] ';
}
errorMessage += '\n';
}
return errorMessage;
}
public static function queryStatus():Void
{
@:privateAccess
var currentStatus = Lib.current.stage.__uncaughtErrorEvents.__enabled;
trace('ERROR HANDLER STATUS: ' + currentStatus);
#if openfl_enable_handle_error
trace('Define: openfl_enable_handle_error is enabled');
#else
trace('Define: openfl_enable_handle_error is disabled');
#end
#if openfl_disable_handle_error
trace('Define: openfl_disable_handle_error is enabled');
#else
trace('Define: openfl_disable_handle_error is disabled');
#end
}
public static function induceBasicCrash():Void
{
throw "This is an example of an uncaught exception.";
}
}