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?
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.
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.
You can use a plugin called Reporter to receive an email of Debug Logs, Stack trace and screen capture on the moment of unhandled Error. Screen capture and stack trace are usually enough to figure out the reason of the Error. For stubborn sneaky Errors you should log more of suspicious data, build and wait again for the error.I Hope this helps.
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:
do not use framework mechanics, but use your own so exception is not caught by framework
make your own class implementing
UnityEngine.ILogHandler
: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)
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.Just set it with reflection. But it's temporary hack and will not work when unity change code.
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
andAppDomain.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:
Debug.LogException
Application.CallLogCallback
which results in firingApplication.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-safeIII.
AppDomain.CurrentDomain.UnhandledException
for example is fired when:You call the following code in editor:
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.
You mentioned Application.RegisterLogCallback, have you tried implementing it? Because the logging callback passes back a stack trace, an error, and an error type (warning, error, etc).
The strategy you outline above would be tough to implement because MonoBehaviours don't just have a single entry point. You'd have to handle OnTriggerEvent, OnCollisionEvent, OnGUI, and so on. Each one wrapping its logic in an exception handler.
IMHO, exception handling is a bad idea here. If you don't immediately re-throw the exception, you'll end up propagating those errors in weird ways. Maybe Foo relies on Bar, and Bar on Baz. Say Baz throws an exception that is caught and logged. Then Bar throws an exception because the value it needs from Baz is incorrect. Finally Foo throws an exception because the value it was getting from Bar is invalid.
Create an empty GameObject in your scene and attach this script to it:
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.