.NET Core 2.1中改进的堆栈信息
. NET Core 2.1 現(xiàn)在具有可讀的異步堆棧信息!使得異步、迭代器和字典 ( key not found ) 中的堆棧更容易追蹤!
這個(gè)大膽的主張意味著什么?
要知道,為了確定調(diào)用 異步 和 迭代器方法的實(shí)際重載,(這在以前)從堆棧信息中跟蹤幾乎是不可能的:
System.Collections.Generic.KeyNotFoundException: The given key '0' was not present in the dictionary.at System.Collections.Generic.Dictionary`2.get_Item(TKey key)at Program.Sequence(Int32 start)+MoveNext()at Program.Sequence(Int32 start, Int32 end)+MoveNext()at Program.MethodAsync()at Program.MethodAsync(Int32 v0)at Program.MethodAsync(Int32 v0, Int32 v1)at Program.MethodAsync(Int32 v0, Int32 v1, Int32 v2)at Program.MethodAsync(Int32 v0, Int32 v1, Int32 v2, Int32 v3)at Program.Main(String[] args)問(wèn)題: “使堆棧信息可讀”
David Kean(@davkean) 于 2017 年 10 月 13 日在dotnet/corefx#24627?提出?使堆棧信息可讀?的問(wèn)題:
如今在 任務(wù) (Task)、異步 (async) 和 等待 (await) 中普遍存在堆棧難以閱讀的現(xiàn)象
對(duì)于在 .NET 中輸出異步的可閱讀堆棧信息已經(jīng)夢(mèng)魂縈繞了5年...
我直到 2017 年 10 月才意識(shí)到這個(gè)問(wèn)題,好在 .NET Core 現(xiàn)在是完全開源的,所以我可以改變它。
作為參考,請(qǐng)參閱文章底部的代碼,它將會(huì)輸出如下的異常堆棧:
System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary. ? at System.ThrowHelper.ThrowKeyNotFoundException() ? at System.Collections.Generic.Dictionary`2.get_Item(TKey key) ? at Program.<Sequence>d__8.MoveNext() ? at Program.<Sequence>d__7.MoveNext() ? at Program.<MethodAsync>d__6.MoveNext()--- End of stack trace from previous location where exception was thrown ---at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() ? at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) ? at Program.<MethodAsync>d__5.MoveNext()--- End of stack trace from previous location where exception was thrown ---at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() ? at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) ? at Program.<MethodAsync>d__4.MoveNext()--- End of stack trace from previous location where exception was thrown ---at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() ? at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) ? at Program.<MethodAsync>d__3.MoveNext()--- End of stack trace from previous location where exception was thrown ---at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() ? at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) ? at Program.<MethodAsync>d__2.MoveNext()--- End of stack trace from previous location where exception was thrown ---at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() ? at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) ? at Program.<Main>d__1.MoveNext()(為簡(jiǎn)潔起見,刪除了行號(hào),如?in C:\Work\Exceptions\Program.cs:line 14)
有時(shí)甚至可見更詳細(xì)的膠水信息:
? at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) ? at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task) at System.Runtime.CompilerServices.TaskAwaiter.GetResult()跟蹤堆棧的一般用途是確定在源代碼中發(fā)生錯(cuò)誤的位置以及對(duì)應(yīng)的路徑。
然而,現(xiàn)如今我們無(wú)法避免異步堆棧,同時(shí)還要面對(duì)很多無(wú)用的噪聲(干擾)。
PR: “隱藏請(qǐng)求中的異常堆棧幀 ”
堆棧信息通常是從拋出異常的地方直接輸出的。
當(dāng)異步函數(shù)拋出異常時(shí),它會(huì)執(zhí)行一些額外的步驟來(lái)確保響應(yīng),并且在延續(xù)執(zhí)行(既定方法)之前會(huì)進(jìn)行清理。
當(dāng)這些額外的步驟被添加到調(diào)用堆棧中時(shí),它們不會(huì)對(duì)我們確定堆棧信息有任何幫助,因?yàn)樗鼈儗?shí)際上是在出現(xiàn)異常?之后?執(zhí)行。
所以它們是非常嘈雜和重復(fù)的,對(duì)于確定代碼在哪里出現(xiàn)異常上并沒(méi)有任何額外的價(jià)值。
實(shí)際產(chǎn)生的調(diào)用堆棧和輸出的不一致:
在刪除這些異常堆棧幀后(隱藏請(qǐng)求中的異常堆棧幀?dotnet/coreclr#14652?),跟蹤堆棧開始變得平易近人:
System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.at System.Collections.Generic.Dictionary`2.get_Item(TKey key)at Program.<Sequence>d__7.MoveNext()at Program.<Sequence>d__6.MoveNext()at Program.<MethodAsync>d__5.MoveNext() --- End of stack trace from previous location where exception was thrown ---at Program.<MethodAsync>d__4.MoveNext() --- End of stack trace from previous location where exception was thrown ---at Program.<MethodAsync>d__3.MoveNext() --- End of stack trace from previous location where exception was thrown ---at Program.<MethodAsync>d__2.MoveNext() --- End of stack trace from previous location where exception was thrown ---at Program.<MethodAsync>d__1.MoveNext() --- End of stack trace from previous location where exception was thrown ---at Program.<Main>d__0.MoveNext()并且輸出的調(diào)用堆棧與實(shí)際的調(diào)用堆棧一致:?
PR: “刪除異步的 Edi 邊界”
異步中的異常使用?ExceptionDispatchInfo?類傳播,這意味著著在每個(gè)連接點(diǎn)都會(huì)有這樣的邊界信息:
--- End of stack trace from previous location where exception was thrown ---
這只是讓你知道兩部分調(diào)用堆棧已經(jīng)合并,并且有個(gè)過(guò)渡。
它如此頻繁地出現(xiàn)在異步中,增加了很多噪音,并沒(méi)有任何附加價(jià)值。
在?刪除異步的 Edi 邊界?dotnet/coreclr#15781?后?所有的?堆棧信息變得有價(jià)值:
System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.at System.Collections.Generic.Dictionary`2.get_Item(TKey key)at Program.<Sequence>d__7.MoveNext()at Program.<Sequence>d__6.MoveNext()at Program.<MethodAsync>d__5.MoveNext()at Program.<MethodAsync>d__4.MoveNext()at Program.<MethodAsync>d__3.MoveNext()at Program.<MethodAsync>d__2.MoveNext()at Program.<MethodAsync>d__1.MoveNext()at Program.<Main>d__0.MoveNext()PR: “處理迭代器和異步方法中的堆棧”
在上一節(jié)中,堆棧已經(jīng)是干凈了,但是要確定是什么情況,還是很困難的一件事。
堆棧中包含著由 C# 編譯器創(chuàng)建的異步狀態(tài)機(jī)的基礎(chǔ)方法簽名,而不僅僅是(你的)源代碼產(chǎn)生的。
你可以確定方法的名稱,但是如果不深入挖掘,則無(wú)法確定所調(diào)用的實(shí)際重載。
在?處理迭代器和異步方法中的堆棧?dotnet/coreclr#14655?之后,堆棧更接近原始來(lái)源:
System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.at System.Collections.Generic.Dictionary`2.get_Item(TKey key)at Program.Sequence(Int32 start)+MoveNext()at Program.Sequence(Int32 start, Int32 end)+MoveNext()at Program.MethodAsync()at Program.MethodAsync(Int32 v0)at Program.MethodAsync(Int32 v0, Int32 v1)at Program.MethodAsync(Int32 v0, Int32 v1, Int32 v2)at Program.MethodAsync(Int32 v0, Int32 v1, Int32 v2, Int32 v3)at Program.Main(String[] args)PR: “實(shí)現(xiàn) KeyNotFoundException 的堆棧追蹤”
因?yàn)橛蓄~外的獎(jiǎng)勵(lì),我著手實(shí)現(xiàn)拋出 “ KeyNotFoundException ” 的堆棧追蹤。
Anirudh Agnihotry (@Anipik) 提出了?實(shí)現(xiàn) KeyNotFoundException 的堆棧追蹤dotnet/coreclr#15201
這意味著這個(gè)異常現(xiàn)在要告訴你哪個(gè) key 找不到的信息:
System.Collections.Generic.KeyNotFoundException: The given key '0' was not present in the dictionary.at System.Collections.Generic.Dictionary`2.get_Item(TKey key)at Program.Sequence(Int32 start)+MoveNext()at Program.Sequence(Int32 start, Int32 end)+MoveNext()at Program.MethodAsync()at Program.MethodAsync(Int32 v0)at Program.MethodAsync(Int32 v0, Int32 v1)at Program.MethodAsync(Int32 v0, Int32 v1, Int32 v2)at Program.MethodAsync(Int32 v0, Int32 v1, Int32 v2, Int32 v3)at Program.Main(String[] args)支持的運(yùn)行時(shí)以及相關(guān)進(jìn)展
這些改進(jìn)將在稍晚的時(shí)間發(fā)布到 Mono 上,并在下一個(gè)階段發(fā)布。但是如果您使用的是較早的運(yùn)行時(shí)版本 (.NET Core 1.0 - 2.0; .NET Framework 或 Mono) 想要獲得一樣的效果,需要使用?Ben.Demystifier?提供的Nuget 包,并且在你的異常中使用?.Demystify()?的方法:
catch (Exception e) {Console.WriteLine(e.Demystify()); }這些改進(jìn)將會(huì)產(chǎn)生與 C#相得映彰的輸出信息,最令人高興的還是全都會(huì)被內(nèi)置!
System.Collections.Generic.KeyNotFoundException: The given key was not present in the dictionary.at TValue System.Collections.Generic.Dictionary<TKey, TValue>.get_Item(TKey key)at IEnumerable<int> Program.Sequence(int start)+MoveNext()at IEnumerable<int> Program.Sequence(int start, int end)+MoveNext()at async Task<int> Program.MethodAsync()at async Task<int> Program.MethodAsync(int v0)at async Task<int> Program.MethodAsync(int v0, int v1)at async Task<int> Program.MethodAsync(int v0, int v1, int v2)at async Task<int> Program.MethodAsync(int v0, int v1, int v2, int v3)at async Task Program.Main(string[] args).NET Core 2.1 將成為 .NET Core 的最佳版本,原因說(shuō)不完,這只是變得更美好的一小步...
上面提到的觸發(fā)異常的代碼及對(duì)應(yīng)的堆棧信息
class Program{ ??static Dictionary<int, int> _dict = new Dictionary<int, int>();
?? ?static async Task Main(string[] args) ? ?{ ? ? ?
?? ? ?try{ ? ? ? ? ?
?? ? ? ?var value = await MethodAsync(1, 2, 3, 4);Console.WriteLine(value);} ? ? ?
?? ? ? ?catch (Exception e){Console.WriteLine(e);}} ?
?static async Task<int> MethodAsync(int v0, int v1, int v2, int v3) ? ?
? ?=> await MethodAsync(v0, v1, v2); ?
??static async Task<int> MethodAsync(int v0, int v1, int v2) ?
? ? ?=> await MethodAsync(v0, v1); ?
???static async Task<int> MethodAsync(int v0, int v1) ? ? ?
?=> await MethodAsync(v0); ? ?
???static async Task<int> MethodAsync(int v0) ? ?
? ?=> await MethodAsync(); ?
???static async Task<int> MethodAsync() ? ?{ ? ? ?
??? ?await Task.Delay(1000); ? ? ?
??? ??int value = 0; ? ? ?
????foreach (var i in Sequence(0, 5)) ? ? ? ?{ ? ? ?
????? ? ?value += i;} ? ? ? ?return value;} ?
???
????static IEnumerable<int> Sequence(int start, int end) ? ?{ ? ?
????? ?for (var i = start; i <= end; i++){ ? ? ? ? ?
????? ? ?foreach (var item in Sequence(i)) ? ? ? ? ? ?{ ?
????? ? ?? ? ? ? ? ? ?yield return item;}}} ?
?? ?static IEnumerable<int> Sequence(int start) ? ?{ ?
????? ? ?? ? ? ? ? ? ?? ? ?var end = start + 10; ? ? ?
????? ? ?for (var i = start; i <= end; i++){_dict[i] = _dict[i] + 1; // Throws exceptionyield return i;}} }
原文地址:https://www.cnblogs.com/chenug/p/8401356.html
.NET社區(qū)新聞,深度好文,歡迎訪問(wèn)公眾號(hào)文章匯總 http://www.csharpkit.com
總結(jié)
以上是生活随笔為你收集整理的.NET Core 2.1中改进的堆栈信息的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: Polly组件对微服务场景的价值
- 下一篇: 在.NET Core中处理一个接口多个不