-
Notifications
You must be signed in to change notification settings - Fork 17.7k
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
proposal: runtime: provide a way to format output in unhandled panics #63455
Comments
@ianlancetaylor How could this proposal be moved into more active consideration? I think it is relatively simple, I am willing to implement it (if accepted), but having it sooner than later could allow the ecosystem to start using it. |
CC @golang/runtime |
What is a situation where PanicError would want to print more information than Error? |
Another possible approach to this would be to add a function to the runtime. // Panicking reports whether the program is crashing due to an unhandle panic.
func Panicking() bool |
I assume that the idea here is that One downside to this approach is that (currently, at least), the runtime calls the (Unless |
So in general I would say that So I think keeping
For the latter case the developer is in control and they can use I really think that |
It may seem simple, but then it raises the question of best practices for which errors implement PanicError versus not, what extra information you print during panics, whether it is acceptable for logging packages and the like to call PanicError to get extra information even for errors that it doesn't know came from panics, and so on. Any time we change a fundamental interface, that's a major shift in the ecosystem that everyone writing Go programs has to absorb. So far I don't think I have seen the evidence that this problem is pressing enough to be worth all this conceptual cost. Small changes can have very large consequences. Also more concretely, if (1) PanicError is used for panics that go all the way to the top level of a goroutine and cause a program crash and (2) the example of what PanicError would do is print a stack trace of the error, then: the panic crash is already printing a stack trace. Why do we need to print a second one too? |
This proposal has been added to the active column of the proposals project |
You are right. Maybe a better interface could be a I do find
There can be a difference between a stack trace where the error was made and a stack trace where the error was passed to panic! You can call into a library which returns errors with stack traces and then determine that error at that point is unexpected and pass it to Also there is much more than just stack traces which can be stored in the error.
Those are similar benefits to why structured logging can be simpler to work with than textual logging, especially when you collect logs to some central place and want to automatically analyze them. Such errors with structured details allow then integration with structured logging, find patterns, count occurrences, etc. But with unhandled panic error, this pattern of not putting everything into |
I still fundamentally don't see why we should privilege the act of crashing the program and make that Error string different from when the panic is recovered but logged for debugging separately. This seems fundamentally wrong as a design matter. If this were needed in a widespread way (I haven't seen evidence of that yet), a more general approach would be to make the runtime look for a Format method and if so call it as if the panic value were being formatted with %+v. But it's also already possible for anything doing panic(err) to do panic(percentPlusVFormatting(err)) where that wrapper intercepts the Error method and returns the %+v text. So it's unclear that the runtime should change. |
Because if panic is recovered, you have control how you want to log the error. But for unhandled panic you, by definition, do not have control. So the other place with control is where the error is made and error could be providing additional way to format itself in the case of the unhandled panic.
Not really, because there is no standard way to really format errors to get different information it might include.
If you control I have used this pattern for JSON marshaling and for extended I mean, this argument could be made also for why does unhandled panic even check for === I think Alternative might be |
This still seems too special case. I don't think we are going to convince you, but I also don't see a compelling case for this change today. |
Yea, I see that we are not getting across to each other. That is fine, no hard feelings here, just so that you know. But I do think that it is a necessary feature, unhandled panics are generally rare and you really want to maximize debugging information you can get at that point. So if error carries such information, it is sad that it is not possible to expose that information. There are not much alternatives (using |
Based on the discussion above, this proposal seems like a likely decline. |
Change https://go.dev/cl/581215 mentions this issue: |
No change in consensus, so declined. |
I think a workaround I can do is to check in |
This CL causes the printing of panic values to ensure that all newlines in the output are immediately followed by a tab, so that there is no way for a maliciously crafted panic value to fool a program attempting to parse the traceback into thinking that the panic value is in fact a goroutine stack. See #64590 (comment) + release note Updates #64590 Updates #63455 Change-Id: I5142acb777383c0c122779d984e73879567dc627 Reviewed-on: https://go-review.googlesource.com/c/go/+/581215 Auto-Submit: Alan Donovan <adonovan@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Michael Pratt <mpratt@google.com>
Background
When code calls
panic(err)
and panic is not handled (recovered) it aborts the whole program while printing the panic value and goroutine stack traces. If the panic value implements theerror
interface, code currently calls itsError
method and prints it out.Proposal
I propose that the code there first checks for the following interface:
And if the value implements this interface,
PanicError
is called instead ofError
to obtain the string to print out.Discussion
I am the author of the
gitlab.com/tozd/go/errors
, a modern drop-in replacement forgithub.com/pkg/errors
which enables errors to be annotated with stack traces and structured details. I noticed that it is pretty common for people to pass error values topanic
. But the issue is that errors with additional optional information do not have any way to output that information when unhanded panic occurs. For example, errors contain a stack trace where they happened but that stack trace is not printed out, nor is available among stack traces printed out by runtime itself (the error might have happened at a completely different place than wherepanic
is called). Errors do print that additional information when usingfmt.Printf
, but that is not used by the runtime.So I suggest that runtime checks for the above interface first, and if it exists, calls it, giving errors an option to provide additional useful information for debugging. The cost of doing this should be minimal (unhandled panics are rare, it is one extra type case in the existing type switch).
Alternatives
Errors could add all extra information to output from
Error()
but generally none of libraries I have seen is doing that. The reason is probably simple: such strings with extra information are hard to combine whileError()
strings generally stay oneliners (except when usingerrors.Join
).The text was updated successfully, but these errors were encountered: