{"id":13430479,"url":"https://github.com/benaadams/Ben.Demystifier","last_synced_at":"2025-03-16T05:30:57.918Z","repository":{"id":37768304,"uuid":"108589744","full_name":"benaadams/Ben.Demystifier","owner":"benaadams","description":"High performance understanding for stack traces (Make error logs more productive)","archived":false,"fork":false,"pushed_at":"2024-03-14T06:29:09.000Z","size":426,"stargazers_count":2760,"open_issues_count":56,"forks_count":118,"subscribers_count":74,"default_branch":"main","last_synced_at":"2024-10-29T15:34:27.084Z","etag":null,"topics":["aspnet-core","dotnet","dotnet-core","error-handling","error-messages","exceptions","stack-traces"],"latest_commit_sha":null,"homepage":"","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/benaadams.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null},"funding":{"github":["benaadams"]}},"created_at":"2017-10-27T19:54:33.000Z","updated_at":"2024-10-19T18:26:59.000Z","dependencies_parsed_at":"2024-01-02T22:44:47.746Z","dependency_job_id":"9138e997-b795-4c00-9ade-895d6b18214d","html_url":"https://github.com/benaadams/Ben.Demystifier","commit_stats":{"total_commits":125,"total_committers":31,"mean_commits":4.032258064516129,"dds":0.6,"last_synced_commit":"8db93654c2869d3bc5ddb1462682f421c99a056b"},"previous_names":[],"tags_count":4,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benaadams%2FBen.Demystifier","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benaadams%2FBen.Demystifier/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benaadams%2FBen.Demystifier/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benaadams%2FBen.Demystifier/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/benaadams","download_url":"https://codeload.github.com/benaadams/Ben.Demystifier/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243707352,"owners_count":20334617,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["aspnet-core","dotnet","dotnet-core","error-handling","error-messages","exceptions","stack-traces"],"created_at":"2024-07-31T02:00:54.405Z","updated_at":"2025-03-16T05:30:57.899Z","avatar_url":"https://github.com/benaadams.png","language":"C#","funding_links":["https://github.com/sponsors/benaadams"],"categories":["Frameworks, Libraries and Tools","C\\#","C#","框架, 库和工具","Exceptions"],"sub_categories":["Exceptions","FPS","异常"],"readme":"# Ben.Demystifier\n[![NuGet version (Ben.Demystifier)](https://img.shields.io/nuget/v/Ben.Demystifier.svg?style=flat-square)](https://www.nuget.org/packages/Ben.Demystifier/)\n[![build](https://github.com/benaadams/Ben.Demystifier/workflows/Demystifier%20PR%20Build/badge.svg)](https://github.com/benaadams/Ben.Demystifier/actions)\n\nOutput the modern C# 7.0+ features in stack traces that looks like the C# source code that generated them rather than IL formatted.\n\n## High performance understanding for stack traces \n\n.NET stack traces output the compiler transformed methods; rather than the source code methods, which make them slow to mentally parse and match back to the source code.\n\nThe current output was good for C# 1.0; but has become progressively worse since C# 2.0 (iterators, generics) as new features are added to the .NET languages and at C# 7.1 the stack traces are esoteric (see: [Problems with current stack traces](#problems-with-current-stack-traces)).\n\n### Make error logs more productive\n\nOutput the modern C# 7.0+ features in stack traces in an understandable fashion that looks like the C# source code that generated them.\n\n[![Demystified stacktrace](https://aoa.blob.core.windows.net/aspnet/stacktrace-demystified.png)](https://aoa.blob.core.windows.net/aspnet/stacktrace-demystified.png)\n\n### Usage\n\n```\nexception.Demystify()\n```\nOr instead of Environment.StackTrace\n```\nEnhancedStackTrace.Current()\n```\nResolves the stack back to the C# source format of the calls (and is an inspectable list of stack frames)\n\nCalling `.ToString()` on the Demystified exception will produce a string stacktrace similar to the following (without the comments):\n\n```csharp\nSystem.InvalidOperationException: Collection was modified; enumeration operation may not execute.\n   at bool System.Collections.Generic.List\u003cT\u003e+Enumerator.MoveNextRare()\n   at IEnumerable\u003cstring\u003e Program.Iterator(int startAt)+MoveNext()                       // Resolved enumerator\n   at bool System.Linq.Enumerable+SelectEnumerableIterator\u003cTSource, TResult\u003e.MoveNext()  // Resolved enumerator\n   at string string.Join(string separator, IEnumerable\u003cstring\u003e values)                    \n   at string Program+GenericClass\u003cTSuperType\u003e.GenericMethod\u003cTSubType\u003e(ref TSubType value) \n   at async Task\u003cstring\u003e Program.MethodAsync(int value)                                  // Resolved async \n   at async Task\u003cstring\u003e Program.MethodAsync\u003cTValue\u003e(TValue value)                       // Resolved async \n   at string Program.Method(string value)+()=\u003e{} [0]                                     // lambda source + ordinal\n   at string Program.Method(string value)+()=\u003e{} [1]                                     // lambda source + ordinal \n   at string Program.RunLambda(Func\u003cstring\u003e lambda)                                       \n   at (string val, bool) Program.Method(string value)                                    // Tuple returning\n   at ref string Program.RefMethod(in string value)+LocalFuncRefReturn()                 // ref return local func\n   at int Program.RefMethod(in string value)+LocalFuncParam(string val)                  // local function\n   at string Program.RefMethod(in string value)                                          // in param (readonly ref)    \n   at (string val, bool) static Program()+(string s, bool b)=\u003e{}                         // tuple return static lambda\n   at void static Program()+(string s, bool b)=\u003e{}                                       // void static lambda\n   at void Program.Start((string val, bool) param)                                       // Resolved tuple param\n   at void Program.Start((string val, bool) param)+LocalFunc1(long l)                    // void local function \n   at bool Program.Start((string val, bool) param)+LocalFunc2(bool b1, bool b2)          // bool return local function \n   at string Program.Start()                                                              \n   at void Program()+()=\u003e{}                                                              // ctor defined lambda  \n   at void Program(Action action)+(object state)=\u003e{}                                     // ctor defined lambda \n   at void Program.RunAction(Action\u003cobject\u003e lambda, object state)                         \n   at new Program(Action action)                                                         // constructor \n   at new Program()                                                                      // constructor \n   at void Program.Main(String[] args)                                                    \n```\n\nCalling `.ToString()` on the same exception would produce the following output\n\n```csharp\nSystem.InvalidOperationException: Collection was modified; enumeration operation may not execute.\n   at System.ThrowHelper.ThrowInvalidOperationException_InvalidOperation_EnumFailedVersion() // ? low value\n   at System.Collections.Generic.List`1.Enumerator.MoveNextRare()                         \n   at Program.\u003cIterator\u003ed__3.MoveNext()                                                   // which enumerator?\n   at System.Linq.Enumerable.SelectEnumerableIterator`2.MoveNext()                        // which enumerator?\n   at System.String.Join(String separator, IEnumerable`1 values)                          \n   at Program.GenericClass`1.GenericMethod[TSubType](TSubType\u0026 value)                     \n   at Program.\u003cMethodAsync\u003ed__4.MoveNext()                                                // which async overload?\n--- End of stack trace from previous location where exception was thrown ---              // ? no value\n   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()                      // ? no value\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) // ? no value\n   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()                           // ? no value\n   at Program.\u003cMethodAsync\u003ed__5`1.MoveNext()                                              // which async overload?\n--- End of stack trace from previous location where exception was thrown ---              // ? no value\n   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()                      // ? no value\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) // ? no value\n   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()                           // ? no value\n   at Program.\u003c\u003ec__DisplayClass8_0.\u003cMethod\u003eb__0()                                         //  ¯\\_(ツ)_/¯\n   at Program.\u003c\u003ec__DisplayClass8_0.\u003cMethod\u003eb__1()                                         //  ¯\\_(ツ)_/¯\n   at Program.RunLambda(Func`1 lambda) \n   at Program.Method(String value)\n   at Program.\u003cRefMethod\u003eg__LocalFuncRefReturn|10_1(\u003c\u003ec__DisplayClass10_0\u0026 )              // local function\n   at Program.\u003cRefMethod\u003eg__LocalFuncParam|10_0(String val, \u003c\u003ec__DisplayClass10_0\u0026 )      // local function\n   at Program.RefMethod(String value)\n   at Program.\u003c\u003ec.\u003c.cctor\u003eb__18_1(String s, Boolean b)                                    //  ¯\\_(ツ)_/¯\n   at Program.\u003c\u003ec.\u003c.cctor\u003eb__18_0(String s, Boolean b)                                    //  ¯\\_(ツ)_/¯\n   at Program.Start(ValueTuple`2 param)                                                   // Tuple param?\n   at Program.\u003cStart\u003eg__LocalFunc1|11_0(Int64 l)                                          // local function\n   at Program.\u003cStart\u003eg__LocalFunc2|11_1(Boolean b1, Boolean b2)                           // local function\n   at Program.Start()\n   at Program.\u003c\u003ec.\u003c.ctor\u003eb__1_0()                                                         //  ¯\\_(ツ)_/¯\n   at Program.\u003c\u003ec__DisplayClass2_0.\u003c.ctor\u003eb__0(Object state)                              //  ¯\\_(ツ)_/¯\n   at Program.RunAction(Action`1 lambda, Object state)\n   at Program..ctor(Action action)                                                        // constructor\n   at Program..ctor()                                                                     // constructor\n   at Program.Main(String[] args)\n```\nWhich is far less helpful, and close to jibberish in places\n\n\n### Problems with current stack traces: \n\n* **constructors** \n\n   Does not match code, output as `.ctor` and `.cctor`\n   \n* **parameters** \n\n   Do not specify qualifier `ref`, `out` or `in`\n   \n* **iterators** \n\n   Cannot determine overload `\u003cIterator\u003ed__3.MoveNext()` rather than `Iterator(int startAt)+MoveNext()`\n* **Linq**\n\n   Cannot determine overload \n   \n   `Linq.Enumerable.SelectEnumerableIterator``2.MoveNext()` \n   \n   rather than\n   \n   `Linq.Enumerable+SelectEnumerableIterator\u003cTSource, TResult\u003e.MoveNext()`\n* **async**\n\n   Cannot determine overload and no modifier such as `async` \n   \n   `\u003cMethodAsync\u003ed__5``1.MoveNext()` \n   \n   rather than\n   \n   `async Task\u003cstring\u003e Program.MethodAsync(int value)`\n\n   Noise!\n   ```\n   --- End of stack trace from previous location where exception was thrown ---\n   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw() \n   at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task)\n   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) \n   at System.Runtime.CompilerServices.TaskAwaiter.ValidateEnd(Task task) \n   at System.Runtime.CompilerServices.TaskAwaiter.GetResult() \n   ```\n\n* **lambdas**\n\n   Mostly jibberish `\u003c\u003ec__DisplayClass2_0.\u003c.ctor\u003eb__0(Object state)` with a suggestion of where they are declared but no hint if there are multiple overloads of the method.\n* **local functions**\n\n   Mostly jibberish `\u003cRefMethod\u003eg__LocalFuncParam|10_0(String val, \u003c\u003ec__DisplayClass10_0\u0026 )` with a suggestion of where they are declared but no hint if there are multiple overloads of the method.\n   \n* **generic parameters**\n\n   Not resolved, only an indication of the number `RunLambda(Func``1 lambda)` rather than `RunLambda(Func\u003cstring\u003e lambda)`\n* **value tuples**\n\n   Do not match code, output as `ValueTuple``2 param` rather than `(string val, bool) param`\n* **primitive types**\n\n   Do not match code, output as `Int64`, `Boolean`, `String` rather than `long`, `bool`, `string`\n* **return types**\n\n   Skipped entirely from method signature\n\n### Benchmarks\n\nTo run benchmarks from the repository root:\n```\ndotnet run -p .\\test\\Ben.Demystifier.Benchmarks\\ -c Release -f netcoreapp2.0 All\n```\n\u003csub\u003eNote: we're only kicking off via `netcoreapp2.0`, benchmarks will run for all configured platforms like `net462`.\u003c/sub\u003e\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenaadams%2FBen.Demystifier","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbenaadams%2FBen.Demystifier","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenaadams%2FBen.Demystifier/lists"}