是否有可能获得与.NET异步方法一个很好的堆栈跟踪? [英] Is it possible to get a good stack trace with .NET async methods?

查看:138
本文介绍了是否有可能获得与.NET异步方法一个很好的堆栈跟踪?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个应用程序的WebAPI下面的示例code设置:

I have the following sample code setup in a WebApi application:

[HttpGet]
public double GetValueAction()
{
    return this.GetValue().Result;
}

public async Task<double> GetValue()
{
    return await this.GetValue2().ConfigureAwait(false);
}

public async Task<double> GetValue2()
{
    throw new InvalidOperationException("Couldn't get value!");
}

可悲的是,当GetValueAction被击中,这回来是堆栈跟踪:

Sadly, when GetValueAction gets hit, the stack trace that comes back is:

    " at MyProject.Controllers.ValuesController.<GetValue2>d__3.MoveNext() in c:\dev\MyProject\MyProject\Controllers\ValuesController.cs:line 61 --- 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.ConfiguredTaskAwaitable`1.ConfiguredTaskAwaiter.GetResult()
at MyProject.Controllers.ValuesController.<GetValue>d__0.MoveNext() in c:\dev\MyProject\MyProject\Controllers\ValuesController.cs:line 56"

因此​​,我得到(错位)GetValue2和getValue的痕迹,但没有提及GetValueAction的。难道我做错了什么?有没有会让我更完整的堆栈跟踪另一种模式?

Thus, I get (mangled) GetValue2 and GetValue in the trace, but no mention of GetValueAction. Am I doing something wrong? Is there another pattern that will get me more complete stack traces?

编辑:我的目标是不写code依靠堆栈跟踪,而是要在异步方法的失败更容易调试

my goal is not to write code relying on the stack trace, but instead to make failures in async methods easier to debug.

推荐答案

首先,堆栈跟踪不这样做大多数人认为他们这样做。他们可以在调试过程中非常有用,但不打算使用运行时,尤其是在ASP.NET。

First off, stack traces don't do what most people think they do. They can be useful during debugging, but are not intended for runtime use, particularly on ASP.NET.

此外,堆栈跟踪技术上约的其中code是久违的,不是的其中code从的来了。通过简单的(同步)code,两者是一样的:code总是返回到任何方法调用它。然而,异步code,这两个是不同的。再次,堆栈跟踪告诉你会发生什么事的下次的,但你有兴趣在发生了什么事情过去

Also, the stack trace is technically about where the code is returning to, not where the code came from. With simple (synchronous) code, the two are the same: the code always returns to whatever method called it. However, with asynchronous code, those two are different. Again, the stack trace tells you what will happen next, but you're interested in what happened in the past.

所以,堆栈帧是不是您需要的正确答案。 埃里克利珀在这里他的回答说明了这一点。

So, the stack frame is not the correct answer for your needs. Eric Lippert explains this well in his answer here.

这@ColeCampbell链接到 MSDN文章介绍追踪因果链的一种方式(其中code传来的的)与异步 code。不幸的是,这种做法是有限的(例如,它不处理叉/加入情形);但是,它是我所知道的,在Windows应用商店的应用程序不工作的唯一办法。

The MSDN article that @ColeCampbell linked to describes one way to track "casuality chains" (where the code came from) with async code. Unfortunately, that approach is limited (e.g., it doesn't handle fork/join scenarios); however, it is the only approach I know of that does work in Windows Store applications.

既然你在ASP.NET中使用完整的.NET 4.5运行时,您可以访问跟踪因果链的更强大的解决方案:逻辑调用上下文。你的异步方法确实有选择加入,虽然如此,你不自由,就像您使用堆栈跟踪得到它。我只是一个尚未发布的一篇博客文章中写了这个,所以你得到preVIEW。 :)

Since you're on ASP.NET with the full .NET 4.5 runtime, you have access to a more powerful solution for tracking casuality chains: the logical call context. Your async methods do have to "opt in", though, so you don't get it for free like you would with a stack trace. I just wrote this up in a blog post that is not yet published, so you're getting a preview. :)

您可以建立一个呼叫的堆栈自己周围的逻辑调用上下文这样:

You can build a "stack" of calls yourself around the logical call context as such:

public static class MyStack
{
  // (Part A) Provide strongly-typed access to the current stack
  private static readonly string slotName = Guid.NewGuid().ToString("N");
  private static ImmutableStack<string> CurrentStack
  {
    get
    {
      var ret = CallContext.LogicalGetData(name) as ImmutableStack<string>;
      return ret ?? ImmutableStack.Create<string>();
    }
    set { CallContext.LogicalSetData(name, value); }
  }

  // (Part B) Provide an API appropriate for pushing and popping the stack
  public static IDisposable Push([CallerMemberName] string context = "")
  {
    CurrentStack = CurrentStack.Push(context);
    return new PopWhenDisposed();
  }
  private static void Pop() { CurrentContext = CurrentContext.Pop(); }
  private sealed class PopWhenDisposed : IDisposable
  {
    private bool disposed;
    public void Dispose()
    {
      if (disposed) return;
      Pop();
      disposed = true;
    }
  }

  // (Part C) Provide an API to read the current stack.
  public static string CurrentStackString
  {
    get { return string.Join(" ", CurrentStack.Reverse()); }
  }
}

ImmutableStack 这里)。然后,您可以使用它是这样的:

(ImmutableStack is available here). You can then use it like this:

static async Task SomeWork()
{
  using (MyStack.Push())
  {
    ...
    Console.WriteLine(MyStack.CurrentStackAsString + ": Hi!");
  }
}

这个方法的好处是,它与的所有异步 code:叉/加入,自定义awaitables, ConfigureAwait(假)等,缺点是您要添加一些开销。此外,这种方法的仅适用于.NET 4.5 ;在.NET 4.0的逻辑调用上下文不是异步 -Aware和意志的不可以正常工作。

The nice thing about this approach is that it works with all async code: fork/join, custom awaitables, ConfigureAwait(false), etc. The disadvantage is that you're adding some overhead. Also, this approach only works on .NET 4.5; the logical call context on .NET 4.0 is not async-aware and will not work correctly.

更新:我发布href=\"http://blog.stephencleary.com/2013/05/announcement-async-diagnostics.html\">的NuGet包(在我的博客中描述的使用PostSharp注入推和自动弹出。因此,获得良好的跟踪应​​该简单得多了。

Update: I released a NuGet package (described on my blog) that uses PostSharp to inject the pushes and pops automatically. So getting a good trace should be a lot simpler now.

这篇关于是否有可能获得与.NET异步方法一个很好的堆栈跟踪?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆