Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

c#

unity3d

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 intention is to catch exceptions in user scripts and also in editor scripts and make sure they are forwarded to a database for analysis (and also to send email to relevant devs so they can fix their shizzle).

In a vanilla C# app I'd have a try-catch around the Main method. In WPF I'd hook one or more of the unhandled exception events. In Unity...?

So far the best I've been able to come up with is something like this:

using UnityEngine; using System.Collections;  public abstract class BehaviourBase : MonoBehaviour {      // Use this for initialization     void Start () {      }      // Update is called once per frame     void Update () {         try         {             performUpdate();             print("hello");         }         catch (System.Exception e)         {             print(e.ToString());         }      }      public abstract void performUpdate();  } 

In other scripts, I derive BehaviourBase instead of MonoBehavior and implement performUpdate() instead of Update(). I haven't implemented a parallel version for Editor clases but I assume I'd have to do the same thing there.

I don't like this strategy, however, because I'll have to backport it to any scripts we grab from the community (and I'll have to enforce it on the team). The Editor scripts don't have a single point of entry comparable to MonoBehavior either, so I assume I'd have to implement exception safe versions of wizards, editors and so on.

I've seen suggestions about catching log messages (as opposed to exceptions) using Application.RegisterLogCallback, but this makes me uncomfortable because I'd need to parse the debug log string rather than having access to the actual exceptions and stacktraces.

So... what's the right thing to do?

like image 565
theodox Avatar asked Feb 25 '13 06:02

theodox


People also ask

How do you handle exceptions globally?

The Controller Advice class to handle the exception globally is given below. We can define any Exception Handler methods in this class file. The Product Service API controller file is given below to update the Product. If the Product is not found, then it throws the ProductNotFoundException class.

What is the best practice for exception handling in C#?

The best practices for Exception Handling in C# are based on logging the exception. The log should be to logging library to keep a record of the exceptions. Log exceptions using log4net, NLog, and other frameworks used for the same purpose.

How do I create a global exception handler?

Adding a Global Exception Handler The New Global Handler window opens. Type in a Name for the handler and save it in the project path. Click Create, a Global Exception Handler is added to the automation project.


2 Answers

Create an empty GameObject in your scene and attach this script to it:

using UnityEngine; public class ExceptionManager : MonoBehaviour {     void Awake()     {         Application.logMessageReceived += HandleException;         DontDestroyOnLoad(gameObject);     }      void HandleException(string logString, string stackTrace, LogType type)     {         if (type == LogType.Exception)         {              //handle here         }     } } 

make sure there is one instance.

The rest is up to you. You can also store the logs in file system, web server or cloud storage.


Note that DontDestroyOnLoad(gameObject) makes this GameObject persistent, by preventing it from being destroyed in case of scene change.

like image 106
Bizhan Avatar answered Sep 17 '22 12:09

Bizhan


There is a working implementation of RegisterLogCallback that I found here: http://answers.unity3d.com/questions/47659/callback-for-unhandled-exceptions.html

In my own implementation I use it to call my own MessageBox.Show instead of writing to a log file. I just call SetupExceptionHandling from each of my scenes.

    static bool isExceptionHandlingSetup;     public static void SetupExceptionHandling()     {         if (!isExceptionHandlingSetup)         {             isExceptionHandlingSetup = true;             Application.RegisterLogCallback(HandleException);         }     }      static void HandleException(string condition, string stackTrace, LogType type)     {         if (type == LogType.Exception)         {             MessageBox.Show(condition + "\n" + stackTrace);         }     } 

I also now have the error handler email me via this routine, so I always know when my app crashes and get as much detail as possible.

        internal static void ReportCrash(string message, string stack)     {         //Debug.Log("Report Crash");         var errorMessage = new StringBuilder();          errorMessage.AppendLine("FreeCell Quest " + Application.platform);          errorMessage.AppendLine();         errorMessage.AppendLine(message);         errorMessage.AppendLine(stack);          //if (exception.InnerException != null) {         //    errorMessage.Append("\n\n ***INNER EXCEPTION*** \n");         //    errorMessage.Append(exception.InnerException.ToString());         //}          errorMessage.AppendFormat         (             "{0} {1} {2} {3}\n{4}, {5}, {6}, {7}x {8}\n{9}x{10} {11}dpi FullScreen {12}, {13}, {14} vmem: {15} Fill: {16} Max Texture: {17}\n\nScene {18}, Unity Version {19}, Ads Disabled {18}",             SystemInfo.deviceModel,             SystemInfo.deviceName,             SystemInfo.deviceType,             SystemInfo.deviceUniqueIdentifier,              SystemInfo.operatingSystem,             Localization.language,             SystemInfo.systemMemorySize,             SystemInfo.processorCount,             SystemInfo.processorType,              Screen.currentResolution.width,             Screen.currentResolution.height,             Screen.dpi,             Screen.fullScreen,             SystemInfo.graphicsDeviceName,             SystemInfo.graphicsDeviceVendor,             SystemInfo.graphicsMemorySize,             SystemInfo.graphicsPixelFillrate,             SystemInfo.maxTextureSize,              Application.loadedLevelName,             Application.unityVersion,             GameSettings.AdsDisabled         );          //if (Main.Player != null) {         //    errorMessage.Append("\n\n ***PLAYER*** \n");         //    errorMessage.Append(XamlServices.Save(Main.Player));         //}          try {             using (var client = new WebClient()) {                 var arguments = new NameValueCollection();                 //if (loginResult != null)                 //    arguments.Add("SessionId", loginResult.SessionId.ToString());                 arguments.Add("report", errorMessage.ToString());                 var result = Encoding.ASCII.GetString(client.UploadValues(serviceAddress + "/ReportCrash", arguments));                 //Debug.Log(result);             }         } catch (WebException e) {             Debug.Log("Report Crash: " + e.ToString());         }     } 
like image 24
Bryan Legend Avatar answered Sep 21 '22 12:09

Bryan Legend