How to get correct stack trace for exceptions in W

2019-04-04 01:30发布

I have a simple API controller method

public async Task<Models.Timesheet> GetByDate(DateTime date, string user = null)
{
    throw new InvalidOperationException();
}

Now the problem is that the exception stack trace I get either in my custom action filter or just by setting IncludeErrorDetailPolicy.Always is like this

System.InvalidOperationException: Operation is not valid due to the current state of the object.
   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.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   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()

It used to be much better with Web API v1. After upgrade to v2 the stack traces are pretty much unusable - it is expected that with async/await the stack traces will not be as they once used to be but in this case the whole stack trace does not include any hint at even the class that failed. Is there something that can be configured in Web API to mitigate the issue?

3条回答
相关推荐>>
2楼-- · 2019-04-04 02:02

I believe stack traces are improved on .NET 4.5.1 when running on Windows 8.1 / Server 2012 R2.

Alternatively, I do have an "Async Diagnostics" NuGet package that you can install into your project. Then add this line:

[assembly: AsyncDiagnosticAspect]

And you can use the ToAsyncDiagnosticString extension method on the Exception type. ToAsyncDiagnosticString includes all the information from ToString and then appends a "logical stack". More documentation (and the source) on GitHub.

Note that there is no support for partial trust and this works best in Debug builds.

查看更多
做自己的国王
3楼-- · 2019-04-04 02:04

So after I asked the question I got the right motivation to properly dig through Web API code to find the issue. It seems that the culprit is ActionFilterAttribute.CallOnActionExecutedAsync method that does something like this (paraphrased, not the actual code):

try
{
    await previous();
}
catch(Exception ex)
{
    exception = ex;
}

var context = new HttpActionExecutedContext(actionContext, exception);
this.OnActionExecuted(context);
if (context.Response == null && context.Exception != null)
    throw context.Exception;

So it happens that the first filter actually gets the correct stack trace. But then it just goes and rethrows the exception, thus losing the original stack trace.

This made me realize that instead of deriving from ExceptionFilterAttribute, I need to derive from ActionFilterAttribute since the former are always called last. Unfortunately I also need to make sure my exception filter runs first but that is not built-in Web API (see this question for a correct solution for this).

My quick solution was to make sure all filters I have in the application (I only have a few) derive from my custom exception filter (and call base.OnActionExecuted()) thus every single filter will actually check for the exception and perform something like this:

public override void OnActionExecuted(HttpActionExecutedContext actionExecutedContext)
{
    if (actionExecutedContext.Exception == null)
        return;

    WriteLog(actionExecutedContext.Exception);

    actionExecutedContext.Response = actionExecutedContext.Request.CreateErrorResponse(
        System.Net.HttpStatusCode.InternalServerError,
        actionExecutedContext.Exception.Message,
        actionExecutedContext.Exception);

    actionExecutedContext.Exception = null;
}

With this workaround I got the correct stack traces both to the log and to the user if error details are turned on.

查看更多
叛逆
4楼-- · 2019-04-04 02:05

This has been fixed in WebApi 2.1 (assembly version 5.1.1)

Follow these instructions to upgrade to the latest version: https://www.nuget.org/packages/Microsoft.AspNet.WebApi

查看更多
登录 后发表回答