public static void registerToUnity(OnError onError, bool logWarnings) { Application.logMessageReceivedThreaded += (message, backtrace, type) => { if ( type == LogType.Assert || type == LogType.Error || type == LogType.Exception || (logWarnings && type == LogType.Warning) ) { ASync.OnMainThread( () => { try { var parsedBacktrace = // backtrace may be empty in release mode. string.IsNullOrEmpty(backtrace) ? BacktraceElem.generateFromHere() : BacktraceElem.parseUnityBacktrace(backtrace); var data = new ErrorData(type, message, parsedBacktrace); onError(data); } catch (Exception e) { // Log at info level so that we wouldn't trigger this handler again. Log.info( $"[{nameof(ErrorReporter)}] Exception in " + $"{nameof(Application)}.{nameof(Application.logMessageReceivedThreaded)}" + $" handler!\n\n{e}" ); } }, // https://fogbugz.unity3d.com/default.asp?832198_48nbh0a3a8cjpr12 runNowIfOnMainThread: false ); } }; }
public void Test1() { BacktraceElem.parseUnityBacktraceLine( "UnityEngine.Debug:LogError(Object)" ).shouldEqual( elem("UnityEngine.Debug:LogError(Object)") ); BacktraceElem.parseUnityBacktraceLine( "com.tinylabproductions.TLPLib.Logger.Log:error(Object) (at Assets/Vendor/TLPLib/Logger/Log.cs:14)" ).shouldEqual( new BacktraceElem( "com.tinylabproductions.TLPLib.Logger.Log:error(Object)", F.some(new BacktraceElem.FileInfo("Assets/Vendor/TLPLib/Logger/Log.cs", 14)) ) ); BacktraceElem.parseUnityBacktraceLine( "Assets.Code.Main:<Awake>m__32() (at Assets/Code/Main.cs:60)" ).shouldEqual( new BacktraceElem( "Assets.Code.Main:<Awake>m__32()", F.some(new BacktraceElem.FileInfo("Assets/Code/Main.cs", 60)) ) ); BacktraceElem.parseUnityBacktraceLine( "com.tinylabproductions.TLPLib.Concurrent.<NextFrameEnumerator>c__IteratorF:MoveNext() (at Assets/Vendor/TLPLib/Concurrent/ASync.cs:175)" ).shouldEqual( new BacktraceElem( "com.tinylabproductions.TLPLib.Concurrent.<NextFrameEnumerator>c__IteratorF:MoveNext()", F.some(new BacktraceElem.FileInfo("Assets/Vendor/TLPLib/Concurrent/ASync.cs", 175)) ) ); }
public static void registerToUnity(OnError onError, bool logWarnings) { Application.logMessageReceivedThreaded += (message, backtrace, type) => { if ( type == LogType.Assert || type == LogType.Error || type == LogType.Exception || (logWarnings && type == LogType.Warning) ) { onError(new ErrorData(type, message, BacktraceElem.parseUnityBacktrace(backtrace).AsReadOnly())); } }; }
public static void registerToUnity(OnError onError, bool logWarnings) { Action <Exception> logExceptionSafe = e => { ASync.OnMainThread( () => { // Log at info level so that we wouldn't trigger this handler again. Log.info( $"[{nameof(ErrorReporter)}] Exception in " + $"{nameof(Application)}.{nameof(Application.logMessageReceivedThreaded)}" + $" handler!\n\n{e}" ); }, // https://fogbugz.unity3d.com/default.asp?832198_48nbh0a3a8cjpr12 runNowIfOnMainThread: false ); }; Application.logMessageReceivedThreaded += (message, backtrace, type) => { if ( type == LogType.Assert || type == LogType.Error || type == LogType.Exception || logWarnings && type == LogType.Warning ) { try { // We want to collect backtrace on the current thread var parsedBacktrace = // backtrace may be empty in release mode. string.IsNullOrEmpty(backtrace) ? BacktraceElem.generateFromHere(1) : BacktraceElem.parseUnityBacktrace(backtrace); var data = new ErrorData(type, message, parsedBacktrace); // But call our error handler on main thread // because handlers are not guaranteed to be thread safe // and Log.info would not work in our handler ASync.OnMainThread( () => { try { onError(data); } catch (Exception e) { logExceptionSafe(e); } }, runNowIfOnMainThread: false ); } catch (Exception e) { logExceptionSafe(e); } } }; }
public void Test2() { var actual = BacktraceElem.parseUnityBacktrace( @"com.tinylabproductions.TLPGame.TLPGame+<>c.<.ctor>b__13_16 () com.tinylabproductions.TLPLib.Components.DebugConsole.DConsoleRegistrar+<>c__DisplayClass4_0.<register>b__0 () com.tinylabproductions.TLPLib.Components.DebugConsole.DConsoleRegistrar+<>c__DisplayClass5_0`1[com.tinylabproductions.TLPLib.Functional.Unit].<register>b__0 (Unit _) com.tinylabproductions.TLPLib.Components.DebugConsole.DConsoleRegistrar+<>c__DisplayClass8_0`2[com.tinylabproductions.TLPLib.Functional.Unit,com.tinylabproductions.TLPLib.Functional.Unit].<register>b__0 (Unit obj) com.tinylabproductions.TLPLib.Components.DebugConsole.DConsoleRegistrar+<>c__DisplayClass9_0`2[com.tinylabproductions.TLPLib.Functional.Unit,com.tinylabproductions.TLPLib.Functional.Unit].<register>b__0 () com.tinylabproductions.TLPLib.Components.DebugConsole.DConsole+<>c__DisplayClass18_0.<showGroup>b__0 () UnityEngine.Events.InvokableCall.Invoke (System.Object[] args)" ); var expected = ImmutableList.Create( elem("com.tinylabproductions.TLPGame.TLPGame+<>c.<.ctor>b__13_16 ()"), elem("com.tinylabproductions.TLPLib.Components.DebugConsole.DConsoleRegistrar+<>c__DisplayClass4_0.<register>b__0 ()"), elem("com.tinylabproductions.TLPLib.Components.DebugConsole.DConsoleRegistrar+<>c__DisplayClass5_0`1[com.tinylabproductions.TLPLib.Functional.Unit].<register>b__0 (Unit _)"), elem("com.tinylabproductions.TLPLib.Components.DebugConsole.DConsoleRegistrar+<>c__DisplayClass8_0`2[com.tinylabproductions.TLPLib.Functional.Unit,com.tinylabproductions.TLPLib.Functional.Unit].<register>b__0 (Unit obj)"), elem("com.tinylabproductions.TLPLib.Components.DebugConsole.DConsoleRegistrar+<>c__DisplayClass9_0`2[com.tinylabproductions.TLPLib.Functional.Unit,com.tinylabproductions.TLPLib.Functional.Unit].<register>b__0 ()"), elem("com.tinylabproductions.TLPLib.Components.DebugConsole.DConsole+<>c__DisplayClass18_0.<showGroup>b__0 ()"), elem("UnityEngine.Events.InvokableCall.Invoke (System.Object[] args)") ); actual.shouldEqual(expected); }