Author Topic: Autocad UnhandledException Handling  (Read 2880 times)

0 Members and 1 Guest are viewing this topic.

Atook

  • Swamp Rat
  • Posts: 1029
  • AKA Tim
Autocad UnhandledException Handling
« on: June 10, 2016, 04:43:33 PM »
I’m researching crash reporting, and looking to implement a service similar to crashalytics in mobile.
Right now I’m testing Raygun, but I’m having some trouble with the implementation.

The recommended implementation in IExtensionApplication.Initialize looks like this:

Code - C#: [Select]
  1. AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException;
  2.  
  3. private static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
  4. {
  5.         _Client.Send(e.ExceptionObject as Exception);
  6. }
  7.  
This doesn’t work, and my understanding is that AutoCAD is catching all unhandled exceptions, so the call never rises up to AppDomain.CurrentDomain.UnhandledException
So I tried adding a handler via FirstChanceException, but that catches all kinds of weird stuff that autocad handles, and so the reporting is throwing errors that aren’t actually affecting the user. That code looks like this:

Code - C#: [Select]
  1. AppDomain.CurrentDomain.FirstChanceException += CurrentDomain_FirstChanceException;
  2.  
  3. private void CurrentDomain_FirstChanceException(object sender, System.Runtime.ExceptionServices.FirstChanceExceptionEventArgs e)
  4. {
  5.         if (e.Exception is Autodesk.AutoCAD.Runtime.Exception)
  6.         {
  7.                 _Client.Send(e.Exception);
  8.         }
  9. }
  10.  
One option is to wrap all my code in try catch blocks, and send errors from there, but this seems it could make things difficult if I decide to switch to a different reporting tool.

Ideally, I could just add an error handler somewhere in IExtensionApplication.Initialize. Any recommendations from those of you that have done this? Not just in how to implement something like this, but in services as well. I’d like to use a service that sends me stack traces and reports on crashes so I know which errors are causing the most problems. Recomendations from swampers who are using a similar tool for AutoCAD projects would be helpful.

CADbloke

  • Bull Frog
  • Posts: 342
  • Crash Test Dummy
Re: Autocad UnhandledException Handling
« Reply #1 on: June 10, 2016, 11:52:48 PM »
The global crash handler works in a RealDwg app but doesn't seem to work for me either in an AutoCAD plugin. I suspect it is because AutoCAD has its own global crash handler that intercepts the errors first, probably long-before they even get to your code. Also, Event subscribers can run in any order they want to so there is no guarantee that your handler will get there first if it is not the only handler subscribed to that event. I think you will have to handle Exceptions in a more granular manner.

My favorite logger is http://serilog.net/ - you can send log messages to any combination of destinations at all sorts of levels. Have you looked into https://github.com/serilog/serilog-sinks-raygun .. or  https://exceptionless.com/pricing/ ? The beauty of Serilog is you can add "sink"s and change the config without touching the rest of your code ... or in XML.

For academic interest (I think it won't work for AutoCAD plugins) I used two global crash handlers because  async crashes need special treatment. I use Serilog, here's my handlers...

Code - C#: [Select]
  1. AppDomain currentDomain = AppDomain.CurrentDomain;
  2. currentDomain.UnhandledException += GlobalCrashHandler;
  3. Dispatcher.UnhandledException += OnDispatcherUnhandledException;
  4.  
  5. // ...
  6.  
  7. /// <summary> Global crash handler. </summary>
  8. /// <param name="sender"> Source of the event. </param>
  9. /// <param name="args">   Unhandled exception event information. </param>
  10. private static void GlobalCrashHandler(object sender, UnhandledExceptionEventArgs args)
  11. {
  12.     Exception e = (Exception)args.ExceptionObject;
  13.     Log.Fatal(e, "Caught by Global Exception catcher. :(");
  14.     CloseTheLog();
  15. }
  16.  
  17. /// <summary> Raises the system. windows. threading. dispatcher unhandled exception event. </summary>
  18. /// <param name="sender"> Source of the event. </param>
  19. /// <param name="e">      Event information to send to registered event handlers. </param>
  20. private static void OnDispatcherUnhandledException(object sender, System.Windows.Threading.DispatcherUnhandledExceptionEventArgs e)
  21. {
  22.     Exception ex = e.Exception;
  23.     Log.Fatal(ex, "Caught by Global Dispatcher Exception catcher. :( : {InnerException}", ex.InnerException);
  24.     CloseTheLog();
  25. }
  26.  
  27.  
  28. private static void CloseTheLog()
  29. {
  30.     if (RealDwgHost.UnresolvedFiles.Any())
  31.         Log.Error("RealDwg could not find the folllowing Files: {UnresolvedFiles}", RealDwgHost.UnresolvedFiles);
  32.  
  33.     Log.Information("Crashed. Closing tvCADdesktop. Sorry. :(");
  34.     Settings.Default.Save();
  35.  
  36.     Log.CloseAndFlush(); // https://github.com/serilog/serilog/wiki/Lifecycle-of-Loggers
  37. }
...yes, I make no attempts to stay alive since if it gets this far all I really want is a post-mortem, keeping it alive would do more harm than good.

Atook

  • Swamp Rat
  • Posts: 1029
  • AKA Tim
Re: Autocad UnhandledException Handling
« Reply #2 on: June 11, 2016, 02:44:56 AM »
..I suspect it is because AutoCAD has its own global crash handler that intercepts the errors first, probably long-before they even get to your code. Also, Event subscribers can run in any order they want to so there is no guarantee that your handler will get there first ...

Thanks for the input CADBloke. That's my understanding as well, CAD is catching the errors before they rise to the domain level. Like you I'm not worried about saving it once it gets to these crash handlers, it's more about having a good post-mortem. I try to catch more critical errors in an internal try/catch.

I'll take a look at serilog, though one of the things I really want is good reporting on errors, the solutions I've looked at so far cost on a monthly basis, but are better written than anything I'd put together.

Keith Brown

  • Swamp Rat
  • Posts: 601
Re: Autocad UnhandledException Handling
« Reply #3 on: June 11, 2016, 12:37:19 PM »
I try to run everything through a commands in my autocad programming. (most everything)  I then wrap all code in the command with a try catch and use that to catch anything that I am not handling.  The same scenario can be used for just about anything.  Unfortunately, it doesnt catch everything but it will catch things that you don't plan for.


I too use serilog for just about everything and run it through a Seq sink so that I can see real time updates in my browser.  I do this for debugging.  In release mode I run it through a file system that is stored on the local computer with a default logging level of Error while in debug mode there is a default logging level of information.  All of this is setup automatically with preprocesser directives and it is working fairly well.  I am contemplating adding a logging level to my settings file so that the user can change the logging level in extreme cases.
« Last Edit: June 11, 2016, 12:41:02 PM by Keith Brown »
Keith Brown | AutoCAD MEP Blog | RSS Feed
AutoCAD MEP 2014 / Revit MEP 2014 / EastCoast CAD/CAM addon / Visual Studio 2013

CADbloke

  • Bull Frog
  • Posts: 342
  • Crash Test Dummy
Re: Autocad UnhandledException Handling
« Reply #4 on: June 12, 2016, 07:27:27 PM »
I try to run everything through a commands in my autocad programming. (most everything)  I then wrap all code in the command with a try catch and use that to catch anything that I am not handling.  The same scenario can be used for just about anything.  Unfortunately, it doesn't catch everything but it will catch things that you don't plan for.

I too use serilog for just about everything and run it through a Seq sink so that I can see real time updates in my browser.  I do this for debugging.  In release mode I run it through a file system that is stored on the local computer with a default logging level of Error while in debug mode there is a default logging level of information.  All of this is setup automatically with preprocesser directives and it is working fairly well.  I am contemplating adding a logging level to my settings file so that the user can change the logging level in extreme cases.

I do a very similar thing. Seq is awesome. For text logs try https://github.com/RolandPheasant/TailBlazer - it is wicked awesome for tailing logs (yes, Linux fanbois, I hear you). I use both and set the log level for the text log lower than for Seq.  Like this in code ...
Code - C#: [Select]
  1. [code=csharp]Log.Logger = new LoggerConfiguration()
  2.     .Enrich.WithProperty("Application", "tvCADdesktop")
  3.     .MinimumLevel.Verbose()
  4.     .WriteTo.Seq("http://localhost:5341/", LogEventLevel.Information)
  5.     .WriteTo.File(AppDataFolder + @"logs\tvCADdesktop.log")
  6.     .CreateLogger();
  7.    
  8. // ...
  9.  
  10. /// <summary>the pathname of the Application Data folder - always has the trailing <c>\</c>
  11. ///  <para />default is <c>"%appdata%\CADbloke\tvCADdesktop\"</c> </summary>
  12. internal static string AppDataFolder => Environment.ExpandEnvironmentVariables(@"%appdata%\CADbloke\tvCADdesktop\");

... or this in XML ...
Code - XML: [Select]
  1. <appSettings>
  2.     <add key="serilog:minimum-level" value="Verbose" />
  3.     <add key="serilog:enrich:with-property:Application" value="tvCADdesktop" />
  4.     <add key="serilog:enrich:with-property:Version" value="1.0" />
  5.     <add key="serilog:using:RollingFile" value="Serilog.Sinks.RollingFile" />
  6.     <add key="serilog:write-to:RollingFile.pathFormat" value="%APPDATA%\CADbloke\tvCADdesktop\Logs\tvCADdesktop-{Date}.log" />
  7.     <add key="serilog:write-to:RollingFile.retainedFileCountLimit" value="15" />
  8.     <add key="serilog:using:Seq" value="Serilog.Sinks.Seq" />
  9.     <add key="serilog:write-to:Seq.serverUrl" value="http://localhost:5341" />
  10.     <add key="serilog:write-to:Seq.restrictedToMinimumLevel" value="Information" />
  11.  </appSettings>

You can add sinks like Raygun etc to your heart's content. The overall minimum level is across all sinks but each sink can have their own config. Structured logging is really taking off in the .NET space. Serilog's creator has a good intro to it: http://nblumhardt.com/2016/06/structured-logging-concepts-in-net-series-1/ - And Nicholas is a really nice guy, smart too: he also created http://autofac.org/
« Last Edit: June 12, 2016, 08:51:03 PM by CADbloke »