What's a good global exception handling strategy for Unity3D?

前端 未结 5 1964
悲&欢浪女
悲&欢浪女 2021-02-01 04:40

I\'m looking into doing some Unity3D scripting stuff, and I\'d like to set up global exception handling system. This is not for running in the release version of the game, the i

5条回答
  •  被撕碎了的回忆
    2021-02-01 05:04

    Unity devs just do not provide us with tools like that. They catch exceptions internally in framework here and there and log them as strings, giving us Application.logMessageReceived[Threaded]. So, if you need exceptions to happen or be logged with your own processing (not unity's) I can think of:

    1. do not use framework mechanics, but use your own so exception is not caught by framework

    2. make your own class implementing UnityEngine.ILogHandler:

      public interface ILogHandler
      {
          void LogFormat(LogType logType, Object context, string format, params object[] args);
          void LogException(Exception exception, Object context);
      }
      

    And use it as said in official docs to log your exceptions. But that way you do not receive unhandled exceptions and exceptions logged from plugins (yes, someone do log exceptions in frameworks instead of throwing them)

    1. Or you can make a suggestion/request to unity to make Debug.unityLogger (Debug.logger is deprecated in Unity 2017) have setter or other mechanism so we can pass our own.

    2. Just set it with reflection. But it's temporary hack and will not work when unity change code.

      var field = typeof(UnityEngine.Debug)
          .GetField("s_Logger", BindingFlags.Static | BindingFlags.NonPublic);
      field.SetValue(null, your_debug_logger);
      

    Note: To get correct stacktraces you need to set StackTraceLogType in editor settings/code to ScriptOnly (most times it's what you need, I wrote an article on how it work) And, when building for iOS, it is said that Script call optimization must be set to slow and safe

    If interested, you can read how popular crash analytics tool works. If you look into crashlytics (crash report tool for android/ios), than you'll find out that it internally uses Application.logMessageReceived and AppDomain.CurrentDomain.UnhandledException events to log managed C# exceptions.

    If interested in examples on unity framework catching exceptions, you may look at ExecuteEvents.Update And another article from me with testing it catching exception in button click listener can be found here.

    Some summary on official ways to log unhandled exception:

    I. Application.logMessageReceived is fired when exception happens on main thread. There are ways for it to happen:

    1. exception caught in c# code and logged through Debug.LogException
    2. exception caught in native code (probably c++ code when using il2cpp). In that case native code calls Application.CallLogCallback which results in firing Application.logMessageReceived

    Note: StackTrace string will contain "rethrow" when original exception have inner exceptions

    II. Application.logMessageReceivedThreaded is fired when exception happens on any thread, including main (it's said in docs) Note: it must be thread-safe

    III. AppDomain.CurrentDomain.UnhandledException for example is fired when:

    You call the following code in editor:

     new Thread(() =>
        {
            Thread.Sleep(10000);
            object o = null;
            o.ToString();
        }).Start();
    

    But it causes crash on android 4.4.2 release build when using Unity 5.5.1f1

    Note: I reproduced some bugs with unity missing stackframes when logging exceptions and assertions. I submited one of them.

提交回复
热议问题