-
Notifications
You must be signed in to change notification settings - Fork 353
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Sanitizing stack traces to improve readability #1051
Conversation
@@ -1,70 +0,0 @@ | |||
// Copyright (c) .NET Foundation. All rights reserved. | |||
// Licensed under the MIT License. See License.txt in the project root for license information. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consolidated this into the other ExceptionExtensions
in the Extensions folder.
|
||
if (additionalDetails == null || additionalDetails.Count == 0) | ||
{ | ||
return ExceptionFormatter.GetFormattedException(exception); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This now uses the GetFormattedException
method as opposed to just calling ToString
else | ||
{ | ||
StringBuilder builder = new StringBuilder(); | ||
builder.AppendLine(ExceptionFormatter.GetFormattedException(exception)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same here
@@ -124,7 +124,7 @@ public Task SaveAndCloseAsync(FunctionInstanceLogEntry item, CancellationToken c | |||
{ | |||
// 0123456789 | |||
// abcdefghij | |||
str = "..." + str.Substring(str.Length - (FunctionInstanceLogEntry.MaxLogOutputLength - 3)); | |||
str = str.Substring(0, FunctionInstanceLogEntry.MaxLogOutputLength - 1) + "…"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Minor change, but moved from using ...
to using …
return GetStackForAggregateException(exception, aggregate); | ||
} | ||
|
||
return GetStackForException(exception, includeMessageOnly: false); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In all the grovelling through stack frames, etc. accessing members, etc. I'm wondering if in strange cases (e.g. generated/emittd code, etc.) what the possibilities are for some info to not be there (e.g. null refs) causing things to blow up. Wondering if we shouldn't have a try/catch surrounding all this that defaults to the full stack trace if for some reason our sanitization logic blows up?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
{ | ||
string formattedException = ExceptionFormatter.GetFormattedException(exc); | ||
|
||
Assert.False(Regex.Match(formattedException, "d__.\\.MoveNext()").Success); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it would help see what is actually going on if these tests tested a full string literal, so we can see the true formatting, and it would also help catch errors better. I realize though that line numbers might change over time, making this hard. Perhaps if the test class were in its own file, so line numbers don't jump around often? or perhaps if a regex is used to match any line numbers?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have some additional tests validating against literal strings for expected traces.
{ | ||
if (string.Equals(arg, "Test1")) | ||
{ | ||
Run1Async().Wait(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we have some non async tests too?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
Massive improvement, great work! |
@fabiocav Nice work! Does it applies to F# too? Might be good to add a smoke test for that? |
There are two goals with this PR:
Improve readability of exception messages/stack traces
This is particularly problematic for Azure Functions users. With this changes, we are sanitizing the exception stack trace prior to logging to:
async
AggregateExceptions
and the associated inner exceptionsHere's an example of the expected output with the same exception:
Before formatting:
After formatting:
Avoid losing important stack information in logs
Changed the log truncation strategy to preserve the top of the stack as, in most customer cases, the top frames contain the relevant information and exception details. This issue is also mitigated by the change to sanitize messages as much less information is lost due to truncation.