-
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: fmt: don't recover panics except for dereferencing nil fmt.Stringer receivers #28150
Comments
fmt.Stringer
receivers
The fmt package tries hard to do something always. The idea is that if you're printing something, and there's a problem, the problem is likely due to code that hasn't run before and/or is hard to get to run at all, so it's better to print something than crash or return error. I'd like to keep it that way. It's worth not crashing a program because a log statement has a bad argument, for example. I admit this can cause problems sometimes, but all decisions do, and your proposal adds complexity and removes utility in the aid of relatively rare cases. The tradeoff doesn't feel right. |
If the problem is “due to code that hasn't run before and/or is hard to get to run at all,” that seems to make it even more important to preserve the stack trace that led to it — which is exactly the information that |
The (Users often omit error checks for |
I know what you're trying to say, but I disagree. I'd rather see bogus output from a rare log statement than crash a production job. I do not want to change this behavior in the fmt package. |
Also worth noting that One of the big points in favor was that There's one big difference, however. If |
I just realised that my last comment was completely redundant given @bcmills' earlier comment; apologies for not reading the thread properly. |
Why don't we make fmt show the stack trace as well instead of just the panic value? That would be a reasonable middle ground. |
E.g. this code (https://play.golang.org/p/g0_AlCKKOa3) right now produces
Along with the error message, it'd include a full stack trace to make it easier to debug. |
This proposal has been added to the active column of the proposals project |
Stack trace includes internal identifiers and paths, so putting them into |
I am not in favor of adding stack traces automatically, as I always groan when I get dumped a stack trace by some program I'm not responsible for. Perhaps under a flag or something, but not by default, please. Also, how would you do that? The |
Based on the discussion above, this proposal seems like a likely decline. |
I agree that adding stack traces to The goal of this proposal is to make a common class of bugs more likely to be found (in tests — especially fuzz tests — and in server logs), and thus more likely to be fixed before they cause real damage (like corrupting stored production data). Adding stack traces to |
The counterargument is that a lot of the time when you are debugging a mysterious problem, new prints get exercised. The last thing you want is to lose the info being printed - or for the whole server to crash - just because of a bad print. It is almost always better to format something and see a weird-looking print in a log than to lose the trace of the problem as a whole. I've seen that happen in other systems (not Go, since we guarded against this). Have you seen the kind of corruption you are hypothesizing caused by fmt recovering panics in real systems? |
This proposal has been added to the active column of the proposals project |
I've been wondering if this means that this program https://go.dev/play/p/Cg-49ZzTcjn should not crash (even though the crash is implied by the documentation of fmt). |
I'm more sympathetic to that argument for functions that are specific to logging, such as in the
I have definitely seen deadlocks caused by I don't have any handy examples of data corruption, although I'm also not entirely sure how to go about looking for those. (Although I have been maintaining Go programs for a long time, I haven't maintained many production systems in Go that produced long-lived or user-facing data using |
I know I'm unlikely to sway anyone, but I'll leave the obligatory "I don't think we should recover any panics" comment. I do occasionally get frustrated having to debug a panic in a So if it where up to me, |
I looked into Google bug 8658139, which turns out to have been about html/template, not fmt. Specifically, a method was being called in a template to create a web page, and it panicked while holding a lock, which made future attempts to render that specific page block forever. Now, it may be that in this specific instance html/template had called fmt, which ate the panic, but if fmt hadn't eaten the panic, then html/template would have instead (via text/template). And if html/template hadn't, then net/http would have. That is, there were three different packages stacked up, all of which would have recovered the panic. The code in question was only reading a data structure, and it should have used defer to unlock its lock. (This was in the bad old days when people avoided defer due to perceived performance problems. Those were never very large, and now they are completely gone.) The fundamental question is whether it is permissible for Go packages to defend against problems in the code they call by recovering from panics. There are good arguments on both sides.
Personally I think the arguments come out in favor of "in favor", although I could be convinced they are merely balanced. Either way, that suggests we should preserve the status quo rather than make a very significant semantic change in the many packages that recover from panics: not just fmt, but also text/template, net/http, and probably others I am forgetting. I admit the technical incorrectness of recovering, but pragmatically it seems to be much better than not. |
Based on the discussion above, this proposal seems like a likely decline. |
No change in consensus, so declined. |
When a type passed to a
fmt
function implementsfmt.Stringer
, thefmt
packagerecover
s any panics that theString
method produces.That's helpful for the common case of nil pointers (https://play.golang.org/p/YbmlVGcfPvO), but in other cases masks real, important bugs — and can lead to unexpected deadlocks (https://play.golang.org/p/GjOv8M75GqG; see also #25245).
I propose that, for Go 2, the
fmt
package should only recover a panic from afmt.Stringer
if:fmt.Stringer
is anil
value of a pointer type, andpanic
call or a dereference of some other value).Enforcing the latter condition may require deeper runtime support.
The text was updated successfully, but these errors were encountered: