あなたが見つけた記事は、コール スタックが私たちのほとんどが思っているように機能しない理由をうまく説明しています。技術的には、コール スタックは、現在のメソッドの後にコードが戻る場所を示すだけです。言い換えれば、コールスタックは「コードがどこから来たのか」ではなく、「コードがどこへ行くのか」です。
興味深いことに、この記事では解決策について言及していますが、詳しくは説明していません。解決策を詳しく説明するブログ投稿がありCallContext
ます。基本的に、論理呼び出しコンテキストを使用して、独自の「診断コンテキスト」を作成します。
CallContext
この記事で紹介したソリューションよりも、このソリューションの方が気に入っています。これは、すべての形式のasync
コード ( のような fork/join コードを含むTask.WhenAll
) で機能するためです。
これは私が知っている最善の解決策です (プロファイリング API にフックするなど、非常に複雑なことを行う場合を除きます)。CallContext
アプローチの注意事項:
- .NET 4.5 フルでのみ動作します。Windows ストア アプリ、.NET 4.0 などはサポートされません。
- コードを手動で「インストルメント化」する必要があります。私の知る限り、自動的に注入する方法はありません。
- 例外は、論理呼び出しコンテキストを自動的にキャプチャしません。したがって、このソリューションは、例外がスローされたときにデバッガーに侵入する場合には問題なく機能しますが、別の場所で例外をキャッチしてログに記録するだけの場合にはあまり役に立ちません。
コード (不変コレクション NuGet ライブラリに依存):
public static class MyStack
{
private static readonly string name = Guid.NewGuid().ToString("N");
private static ImmutableStack<string> CurrentContext
{
get
{
var ret = CallContext.LogicalGetData(name) as ImmutableStack<string>;
return ret ?? ImmutableStack.Create<string>();
}
set
{
CallContext.LogicalSetData(name, value);
}
}
public static IDisposable Push([CallerMemberName] string context = "")
{
CurrentContext = CurrentContext.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;
}
}
// Keep this in your watch window.
public static string CurrentStack
{
get
{
return string.Join(" ", CurrentContext.Reverse());
}
}
}
使用法:
static async Task SomeWorkAsync()
{
using (MyStack.Push()) // Pushes "SomeWorkAsync"
{
...
}
}
更新: PostSharp を使用してプッシュとポップを自動的に挿入するNuGet パッケージ (ブログで説明)をリリースしました。したがって、適切なトレースを取得することは、はるかに簡単になるはずです。