Catch all exception in Unity - c#

I've got a Unity mobile game in the app stores and I've got a function that sends emails from the players to my server, to notify me that stuff happened, that is not supposed to happen. If e.g. a try that I wrote fails, I send myself an email to see, that there must be a bug.
Is it possible, to build something like an OnException handler, that sends me an email on every exception that occurs, including code I didn't write? Some exceptions may come from plugins that I use, so I would like to have some sort of Listener, that does stuff whenever any sort of exception get's called.
Is that possible?

You can't catch all other exceptions in other plugins. Unity does that in the background and Unity gives you API to receive the exceptions it caught:
Newer version of Unity:
void OnEnable()
{
Application.logMessageReceived += LogCallback;
}
//Called when there is an exception
void LogCallback(string condition, string stackTrace, LogType type)
{
//Send Email
}
void OnDisable()
{
Application.logMessageReceived -= LogCallback;
}
Older version of Unity:
void OnEnable()
{
Application.RegisterLogCallback(LogCallback);
}
//Called when there is an exception
void LogCallback(string condition, string stackTrace, LogType type)
{
//Send Email
}
void OnDisable()
{
Application.RegisterLogCallback(null);
}
There is also logMessageReceivedThreaded.
EDIT:
Stacktrace issue:
If the stackTrace parameter is empty then you can use the System.Diagnostics.StackTrace class to manually retrieve the stacktrace yourself while still inside the LogCallback function. For example,
void LogCallback(string condition, string stackTrace, LogType type)
{
//Send Email
System.Diagnostics.StackTrace sTrace = new System.Diagnostics.StackTrace();
Debug.Log(sTrace.ToString());
}

Try this solution, should work for your case:
http://answers.unity3d.com/questions/47659/callback-for-unhandled-exceptions.html

Check out Hockey App. Really nice tool, has Unity sdk and does exactly what you need. Every Debug.LogError or Debug.LogException is getting reported there. So you can be notified not only about code exceptions but also some data missing or anything you would like to report as error.

I tried to answer a question that was marked as a duplicate to this one ( How to detect if there is an error in unity c# when running without see the console? ), however it's been closed, so i'm adding an answer here.
I wanted to print a message directly to the screen as using the debug log wasn't going to work in my case. This was the elegant solution I found (which uses Android's default ShowDialog):
using (AndroidJavaClass androidJavaClass = new AndroidJavaClass("com.cattlecall.unityandroidaccessor.UnityAndroidDialog"))
{
androidJavaClass.CallStatic("ShowDialog", new object[]
{
"PORT",
port.ToString(),
"yis'm",
"NOOOOO"
});
}

Related

Foreach is stopped when exception is caught in try catch block located within the loop in ASP.NET Webapi

In my API I'm calling external SOAP API method several times. In order to achieve this I use foreach loop and inside it I put try catch block to handle exception and continue the loop. Everything works fine on my machine but when I deploy the API to another server running IIS it seems to stop calling the external API's method when exception is thrown as if the try catch was suddenly moved outside of the loop. Is it possible that it might have something to do with IIS configuration?
I've already tried putting this method inside another one and then putting that method inside try catch block but it didn't help.
public class Loader
{
private static SoapClient client;
private static string AddItems(Order order)
{
foreach(item in order.items)
{
try
{
client.SoapMethod(item);
}
catch(Exception e)
{
// Log error and go to next iteration
Log.LogError(e.Message);
}
}
}
}
Log class uses Log4Net to put error message in a text file:
public class Log
{
private static readonly ILog log = LogManager.GetLogger("API");
public static void LogError(string message)
{
log.Error(message);
}
}
In case the external SOAP API's method throws exception it should be skipped and it's supposed to go to the next iteration of the foreach loop to call this method with another data.
EDIT: I deployed my API to another server and it works there without breaking the loop so it seems that there's something wrong with that particular machine.
Thanks to the comment of #Silvermind I found out what the problem was. Logs were also sent to Windows EventLog which threw IOException every time any application tried to save logs in Application source (from which a custom view was created). I googled it and it appears to be a known issue in all Windows systems from Windows 7 SP1 and Windows Server 2008 SP2 as described on this site. The same error can be seen while filtering the Application view in Event Viewer.

What's a good global exception handling strategy for 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?
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.
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());
}
}
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:
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)
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.
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:
exception caught in c# code and logged through Debug.LogException
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.
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.
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.

Windows service Started and stopped automatically, exception handling issue

I have developed a 32 bit service, I am running it in Windows 7 Home Premium x64.
Problem is when I start it, windows gives me the following message
The WLConsumer service on Local Computer started and then stopped. Some services stop
automatically if they are not in use by other services or programs.
I found the following message in the event log
Service cannot be started. System.ArgumentException: Log WLConsumer has already been registered as a source on the local computer.
at System.Diagnostics.EventLogInternal.CreateEventSource(EventSourceCreationData sourceData)
at System.Diagnostics.EventLogInternal.VerifyAndCreateSource(String sourceName, String currentMachineName)
at System.Diagnostics.EventLogInternal.WriteEntry(String message, EventLogEntryType type, Int32 eventID, Int16 category, Byte[] rawData)
at System.Diagnostics.EventLog.WriteEntry(String message, EventLogEntryType type)
at WeblogicConsumerService.WeblogicConsumer.winEventlogMe(String logTxt, String logSrc, Char entryType) in C:\Program Files (x86)\CSI\WeblogicConsumerService\WeblogicConsumer.cs:line 136
at WeblogicConsumerService.WeblogicConsumer.OnStart(String[] args) in C:\Program Files (x86)\CSI\WeblogicConsumerService\WeblogicConsumer.cs:line 63
at System.ServiceProcess.ServiceBase.ServiceQueuedMainCallback(Object state)
This is my codeblock in the OnStart() method
protected override void OnStart(string[] args)
{
#region WEBLOGIC CREDENTIALS
try
{
//Weblogic URL
this.url = Registry.LocalMachine.OpenSubKey(#"Software\CSI_WL").GetValue("URL").ToString();
//Queue name
this.qName = Registry.LocalMachine.OpenSubKey(#"Software\CSI_WL").GetValue("Queue").ToString();
//Weblogic login name
this.user = Registry.LocalMachine.OpenSubKey(#"Software\CSI_WL").GetValue("User").ToString();
//Weblogic password
this.pwd = Registry.LocalMachine.OpenSubKey(#"Software\CSI_WL").GetValue("Pwd").ToString();
//Weblogic Connection Factory
this.cfName = Registry.LocalMachine.OpenSubKey(#"Software\CSI_WL").GetValue("ConnectionFactory").ToString();
//root folder
this.rFolder = Registry.LocalMachine.OpenSubKey(#"Software\CSI_WL").GetValue("root").ToString();
}
catch (Exception e)
{
winEventlogMe(e.Message, "WLRegistryKeys", 'e');
}
#endregion
winEventlogMe("Successful start", "SeriviceStartup", 'i');
synchro.Enabled = true;
}
winEventLogMe is the method I am calling for logging.
public static void winEventlogMe(string logTxt, string logSrc, char entryType)
{
#region Log
//Log to event log
EventLog theEvent = new EventLog("WLConsumer");
theEvent.Source = logSrc;
if (entryType == 'e')
theEvent.WriteEntry(logTxt, EventLogEntryType.Error);
else if (entryType == 'i')
theEvent.WriteEntry(logTxt, EventLogEntryType.Information);
else if (entryType == 'w')
theEvent.WriteEntry(logTxt, EventLogEntryType.Warning);
else
theEvent.WriteEntry(logTxt, EventLogEntryType.Error);*/
#endregion
}
When I comment out the calls to winEventLogMe() method in the OnStart() method, the service starts without an error. So obviously something is wrong with the winEventLogMe() method.
Can someone please help me figure out whats the problem as I am totally clueless on how to solve this issue now.
thanx in advance :)
#nick_w I have edited my code as you suggested, the service started succesfully. But on Stopping it I got the following message:
Failed to stop service. System.ArgumentException: The source 'WLConsumer2012' is not registered in log 'ServiceStop'. (It is registered in log 'SeriviceStartup'.) " The Source and Log properties must be matched, or you may set Log to the empty string, and it will automatically be matched to the Source property.
at System.Diagnostics.EventLogInternal.VerifyAndCreateSource(String sourceName, String currentMachineName)
at System.Diagnostics.EventLogInternal.WriteEntry(String message, EventLogEntryType type, Int32 eventID, Int16 category, Byte[] rawData)
at System.Diagnostics.EventLog.WriteEntry(String message, EventLogEntryType type)
at WeblogicConsumerService.WeblogicConsumer.winEventlogMe(String logTxt, String logSrc, Char entryType) in C:\Program Files (x86)\CSI\WeblogicConsumerService\WeblogicConsumer.cs:line 139
at WeblogicConsumerService.WeblogicConsumer.OnStop() in C:\Program Files (x86)\CSI\WeblogicConsumerService\WeblogicConsumer.cs:line 70
at System.ServiceProcess.ServiceBase.DeferredStop()
here is the OnStop() method
protected override void OnStop()
{
winEventlogMe("Successful stop", "ServiceStop", 'i');
}
These event logs are starting to confuse me a lot. I have done the same method of logging in other services and never encountered such problems. How can I be getting these errors in this service yet its not much different from all the others I have done :(
I think this is your problem:
EventLog theEvent = new EventLog("WLConsumer");
Judging by the exception, I am thinking that WLConsumer is the name of the event source. What this means is that you might be better off with this:
EventLog theEvent = new EventLog(logSrc);
theEvent.Source = "WLConsumer";
This is just using the parameters the other way around.
If I do a little decompilation, there is a check like this:
if (!EventLogInternal.SourceExists(logName, machineName, true))
In your case I would think this check is returning true, meaning that it is trying to create a log named WLConsumer but failing because WLConsumer has been registered as an event source.
Edit:
When I have used the event log in the past, I wrote everything to the same combination of source and log. In your case you seem to be using a different combination of source and log each time you write an entry.
From MSDN (emphasis mine):
If you write to an event log, you must specify or create an event Source. You must have administrative rights on the computer to create a new event source. The Source registers your application with the event log as a valid source of entries. You can only use the Source to write to one log at a time. The Source can be any random string, but the name must be distinct from other sources on the computer. It is common for the source to be the name of the application or another identifying string. An attempt to create a duplicated Source value throws an exception. However, a single event log can be associated with multiple sources.
What I would suggest is this:
Use WLConsumer (or WLConsumer2012) as your source, and either
Define your own log, 'WLConsumerServiceEventLog` or something; or
Leave the log blank. They go into the Application log in this case.
Regardless, standard practice seems to be to do something like this prior to running your service for the first time, such as in an installer (copied straight from above link):
// Create the source, if it does not already exist.
if(!EventLog.SourceExists("MySource"))
{
//An event log source should not be created and immediately used.
//There is a latency time to enable the source, it should be created
//prior to executing the application that uses the source.
//Execute this sample a second time to use the new source.
EventLog.CreateEventSource("MySource", "MyNewLog");
Console.WriteLine("CreatedEventSource");
Console.WriteLine("Exiting, execute the application a second time to use the source.");
// The source is created. Exit the application to allow it to be registered.
return;
}
Note the point in the comments re latency. The logs are not necessarily created immediately, so it pays to code with this in mind. You could also use the EventLogInstaller to create the log. This may be the easier option if you are using an installer to deploy your service.
It is critical not to overload the on start method and in order to prevent service start failures, normally the onstart method launches the main code as a separate thread

How to prevent path from being attached if condition not required? SSIS custom component

In my custom data flow component, I have overriden the OnOutputPathAttached method. I want outputs to be attached under certain conditions. ie:
public override void OnOutputPathAttached(int outputID)
{
if (/*condition*/)
{
//do some processing
base.OnOutputPathAttached(outputID);
}
else
{
System.Windows.Forms.MessageBox.Show("Error message");
//CODE TO STOP OUTPUT FROM BEING ATTACHED???
}
}
What should I put so that the output isn't attached? For now it shows the error message but still attaches the output.
I suspect that it's too late to stop the user from attaching a path by the time your OnOutputPathAttached method is called. (At least, that's how I'm reading the MSDN page on the AttachPathAndPropagateNotifications method.)
Your best bet seems to be to return VS_ISBROKEN from your Validate method, along with raising useful OnError events.

C# - Exception Propagation Issue in AsyncCallback

The following code is pretty self-explanatory and my question is very simple :
Why is AsyncCallback method "HandleConnect" not propagating exception to the "Connect" method and how to propagate it ?
public void Connect(IPEndPoint endpoint, IVfxIpcSession session)
{
try
{
ipcState.IpcSocket.BeginConnect(ipcState.IpcEndpoint, HandleConnect, ipcState);
}
catch(Exception x)
{
ManageException(x.ToString()); //Never Caught, though "HandleConnect" blows a SocketException
}
}
private void HandleConnect(IAsyncResult ar)
{
// SocketException blows here, no propagation to method above is made.
// Initially there was a try/catch block that hided it and this is NOT GOOD AT ALL
// as I NEED TO KNOW when something goes wrong here.
ipcState.IpcSocket.EndConnect(ar);
}
1 - I guess this is pretty normal behavior. But I would appreciate a comprehensive explanation of why is this happening this way and what happens exactly behind the hoods.
2 - Is there any (quick and simple) way to propagate the exception through my app ?
forewarning I know many dudes in here are very critical and I anticipate the comment "Why don't you put the ManageException directly in the "HandleConnect" Method. Well, to make a long story short, let's just say "I got my reasons" lol. I just posted a code sample here and I want to propagate this exception way further than that and do much more stuff than showed in other places in the "N-upper" code.
EDIT
As an aswer to a comment, I also tried this previously indeed, with no luck :
private void HandleConnect(IAsyncResult ar)
{
try
{
ipcState.IpcSocket.EndConnect(ar);
}
catch(Exception x)
{
throw x; // Exception Blows here. It is NOT propagated.
}
}
My Solution :
I ended up putting an Event Handler to whom every concerned code logic subscribes.
This way the exception is not just swallowed down nor just blows, but a notification is broadcasted.
public event EventHandler<MyEventArgs> EventDispatch;
private void HandleConnect(IAsyncResult ar)
{
try
{
ipcState.IpcSocket.EndConnect(ar);
}
catch(Exception x)
{
if (EventDispatch!= null)
{
EventDispatch(this, args);
}
}
}
//Priorly, I push subscriptions like that :
tcpConnector.EventDispatch += tcpConnector_EventDispatch;
public void tcpConnector_EventDispatch(object sender, VfxTcpConnectorEventArgs args)
{
//Notify third parties, manage exception, etc.
}
This is a little bit crooked, but it works fine
When you use BeginConnect the connection is done asynchronously. You get the following chain of events:
Connect "posts" a request to connect through BeginConnect.
Connect method returns.
The connection is done in the background.
HandleConnect is called by the framework with the result of the connect.
When you reach step number 4, Connect has already returned so that try-catch block isn't active any more. This is the behavior you get when using asynchronous implementations.
The only reason you would have an exception caught in Connect is if BeginConnect fails to initiate the background connection task. This could e.g. be if BeginConnect validates the supplied arguments before initiating the background operation and throws an exception if they are not correct.
You can use the AppDomain.UnhandledException event to catch any unhandled exceptions in a central place. Once the exception reaches that level any form of recovery is probably hard to achieve, since the exception could be from anywhere. If you have a recovery method - catch the exception as close to the origin as possible. If you only log/inform the user - catching centrally in once place is often better.
One option is to use AsyncWaitHandle with your existing code.
For better exception handling, you would have to either use event based programming model or modify your code to use BackgroundWorker component which supports reporting error from the worker thread to the main thread.
There are some discussions and articles present on this topic at following links:
http://openmymind.net/2011/7/14/Error-Handling-In-Asynchronous-Code-With-Callbacks/
MSDN Sample: http://msdn.microsoft.com/en-us/library/ms228978.aspx
Further to what Anders has pointed out, it is probably a good idea to read this:
http://msdn.microsoft.com/en-us/library/2e08f6yc.aspx
and look into how you can pass a callback method into the asynchronous call to BeginConnect (if one does exist) using something like an AsyncCallback where you can retrieve the delegate and call EndInvoke within a try catch block.
E.g.:
public void
CallbackMethod
(IAsyncResult AR)
{
// Retrieve the delegate
MyDelegate ThisDelegate =
(MyDelegate)AR.AsyncState;
try
{
Int32 Ret = ThisDelegate.EndInvoke(AR);
} // End try
catch (Exception Ex)
{
ReportException(Ex);
} // End try/catch
} // End CallbackMethod

Categories

Resources