Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Debugging Exceptions in Async Code

Back when I used synchronous methods in code using ASP.NET MVC 3, if an exception was thrown, it wasn't hard to figure out where it came from in my exception filter. Just looking at the stack trace was enough.

When an async method in ASP.NET Web API throws an exception, however, the exception details are less useful, since the stack trace doesn't show where the exception was thrown from:

System.ArgumentException: title must not be empty.
Parameter name: title    
at System.Web.Http.Filters.ActionFilterAttribute.<CallOnActionExecutedAsync>d__1.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.Controllers.ActionFilterResult.<ExecuteAsync>d__2.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)
at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
at System.Web.Http.ApiController.<InvokeActionWithExceptionFilters>d__1.MoveNext()

Currently, I have to look at all the exceptions with this message and guess which one was thrown based on the request URI and what's going on behind the scenes, or try and reproduce the bug locally with a debugger attached.

MSDN has an article detailing a couple strategies for getting a casualty chain: http://msdn.microsoft.com/en-us/magazine/jj891052.aspx3. From my understanding, I have two choices:

  • Use System.Diagnostics.EventListener and take a performance hit at run-time
  • Create an extension method and use it with every await call

Is there a better way to log more helpful details about what threw this exception? Having the casualty chain would be nice, but just getting the exception source would be fine (what type and method this was thrown in).

like image 839
Dustin Avatar asked Dec 20 '13 00:12

Dustin


1 Answers

Both of your options cause run-time overhead.

A third option (which also causes run-time overhead) is to use my AsyncDiagnostics library. I'm partial to that one (naturally), but every option introduces run-time overhead.

like image 166
Stephen Cleary Avatar answered Sep 22 '22 15:09

Stephen Cleary