Skip to content
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

Really controversial topic: completely remove panic/fatal? #207

Closed
bufdev opened this issue Dec 8, 2016 · 5 comments
Closed

Really controversial topic: completely remove panic/fatal? #207

bufdev opened this issue Dec 8, 2016 · 5 comments

Comments

@bufdev
Copy link
Contributor

bufdev commented Dec 8, 2016

I'm sure this won't happen pre-V1, but controversial opinion: I think people use logger.Panic/Fatal way too much, and it breaks execution flow of a program, it's harder to reason about, and now with zap especially, it's weird that there are situations where you can log at panic level but not actually panic. I think if you really want to panic or os.Exit(1), you should do it manually (and rarely, basically in the main function only).

Since zap will be used a lot with services, who should almost never panic anyways, might it encourage good behavior to completely remove this ability, and if someone really wants to panic or exit, they can do it manually?

@akshayjshah
Copy link
Contributor

While I agree that services should use Panic and Fatal sparingly, it's important to support them natively - without this support, service owners not only need to call panic or os.Exit themselves, they also need to sync any open file descriptors, flush Sentry reporting buffers, etc., or they'll lose the reason for the crash. That's difficult to do from many locations in the code without passing a number of file-descriptor-like structs (which zap models as WriteSyncers) around with the loggers.

Unless the service owner is also doing evil things with recover, I don't find that Panic and Fatal make programs more difficult to reason about; it just means that sometimes there's a hard crash.

@jcorbin
Copy link
Contributor

jcorbin commented Dec 9, 2016

If it helps, we're planning to drop support to "log at panic level without panicing" (likewise for fatal) before 1.0 (WIP in #201).

@rogpeppe
Copy link

FWIW I agree with the sentiment of this issue. I don't think that any service should ever call exit, and panic should be reserved for truly exceptional cases. By putting Panic into the logger, ISTM that we're pretending that the only panics that happen are those that are called explicitly, whereas of course panics can happen for other reasons too - I don't think there's a particularly good rationale for saying that one kind of panic should be treated differently from another.

@bufdev
Copy link
Contributor Author

bufdev commented Dec 14, 2016

Ya, I like having at least a "style guide" ban on doing Fatal/Panic. I see a lot of golang developers using Fatal/Panic freely (especially new gophers) without thinking about the implications - I'd do as far as to wish that the stdlib log did not split out into Print/Fatal/Panic method types.

@akshayjshah
Copy link
Contributor

Again, I'm generally sympathetic to this strain of thought. However, I feel strongly that there's an important place for at least panic-level logging in the main method of server processes: if the initial configuration is nonsensical, I'd much prefer a hard crash that signals the scheduling and deployment system to halt rollout. I don't think that a blanket ban on panic- and fatal-level logging is nuanced enough to actually capture best practice here.

It's also strictly worse to force developers who do want to panic or exit to do so manually, since they'll often lose the critical log messages immediately preceding the crash. Integration with the logging library allows us to flush file handles, network buffers, and the like before we actually crash. @peter-edge, this has been a long-running pain point with our internal use of logrus.

In sum: yes, these methods should be used very sparingly, and only in exceptional circumstances. When they are necessary, though, it's better to have them correctly integrated.

bobheadxi added a commit to sourcegraph/sourcegraph-public-snapshot that referenced this issue Apr 22, 2022
This PR removes Sync() from the logger interface and adds a callback returned by log.Init which should be called before application exit. The removal of Sync() from the Logger interface helps indicate that not every logger from log.Scoped needs a call to Sync(). For logtest, we add a t.Cleanup func to sync the global logger.

Additionally, I've noticed that log.Fatal is used quite liberally. Even if we switch to error propagation, there's still the issue that os.Exit will bypass any defer calls and hence potentially miss Sync(). To accommodate this pattern, I've decided to add back Fatal to Logger, which internally calls Sync() before os.Exit(1). Also see uber-go/zap#207 for more rationale for supporting Fatal.

Background: All loggers are children of the root global logger that is instantiated once. As such, it is sufficient to just call Sync() on the root logger - all child loggers just write to underlying loggers until they reach the root, which as far as I can tell is an os.File that has Sync(), which gets called by Zap, so it's not a no-op even if Zap itself does not do any buffering (at the moment, the global logger is not configured to do so)

Aside: in practice, it's honestly probably not a huge deal to never call Sync(), but we should anyway just in case, so hopefully this balances the probable need to call Sync() with ergonomics.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

No branches or pull requests

4 participants