-
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
log: add func Default() *Logger #39057
Comments
You're getting a lot of downvotes, so I'll try to explain mine. You can already configure the global logger via SetFlags, SetOutput, and SetPrefix. You can also query some of those with funcs like like Flags, Writer, and Prefix. This already covers pretty much all reasonable use cases of modifying the global logger. Exporting the variable would have multiple downsides:
|
You implies that we should not have global default implementations, sorry if I am misreading you, but how is this different from The primary reason I am interested in this feature is the ability to (readonly) pass around the default log.Logger implementation for modularity/testing. I get that this opens up unsaftey, but that is due to the lack of // in prod
t := thing.New(log.Default)
t.Do()
// in test
l := &mockLogger{}
t := thing.New(l)
t.Do()
l.Assert() Today you would have to make a dummy implementation that just forwards calls: type StdLogger struct{}
func (l StdLogger) Fatal(v ...interface{}) { log.Fatal(v) }
func (l StdLogger) Fatalf(format string, v ...interface{}) { log.Fatalf(format, v) }
// ... Speaking to your concerns specifically:
Yes, but
Do not. You can also modify
This is more a problem with global mutability, as you see the same issues with |
Maybe it would be safer with package log
func Default() *Logger { return std } but the other packages that use this pattern seem to be fine. |
I am open to this option as well, but agree consistency is probably more important. Also, especially in testing, there are cases where you want to nop out the actual default Logger. I have mucked around with |
I very much understand the desire to pass a *log.Logger; and to use that instead of calling the default log directly. An example, optional logging in a package. Where the caller wants to use the default log. Best they can do is pass a new log object with the same parameters as the default, ie: |
I don't agree that passing around a I think the right solution is an interface, and there is a thread about that in #13182. I think we should focus the discussion and suggestions in that thread; it would make little sense to accept this proposal independently of that earlier one. Regarding the API similarities with other packages - yes, other packages have similar pitfalls, but that doesn't mean they are OK to inherit :) Remember that a lot of packages in the standard library were designed over ten years ago, and they might still carry bad design decisions due to the Go1 compatibility guarantee. I would definitely say that |
That is not necessarily the case: you could wrap the default
I am open to adding an interface too, it just seems orthogonal. I can still make my own interface and get the modularity I want with just the default var Default Interface = New(os.Stderr, "", LstdFlags)
type Interface interface { // or what ever name #13182 decides
Fatal(...interface{})
Fatalf(string, ...interface{})
Fatalln(...interface{})
Flags() int
Output(int, string) error
Panic(...interface{})
Panicf(string, ...interface{})
Panicln(...interface{})
Prefix() string
Print(...interface{})
Printf(string, ...interface{})
Println(...interface{})
SetFlags(int)
SetOutput(io.Writer)
SetPrefix(string)
Writer() io.Writer
} |
I'm skeptical that package log should define an interface, especially not the one in the comment above mine. A package that wants a logger should probably define a one-method interface that it expects. The interface above is no different from using log.Logger directly. |
To restate, the proposal here is to add
I don't see much enthusiasm for this in the comments and reactions above, but let's put it in the minutes and see if more people chime in. |
I've certainly had code that could have been simpler with something like that. |
@jimmyfrasche why would the code have been simpler? What is the use case? We still need specific examples to evaluate whether this is worth the added complexity. |
To inject a logger into a program, so that it can be overridden in tests or by a flag, I need to reconstruct the default logger. With something like this proposal it could just be func main() {
realMain(log.Default())
} |
It seems a bit redundant but is also consistent with what we've done elsewhere (importer.Default, build.Default, net.DefaultResolver) for exposing the default functionality used by top-level functions. Does anyone object to adding |
Based on the discussion above, this seems like a likely accept |
There are places where I declare a logger just so I have a struct to pass around. If this were available, I would call |
No change in consensus, so accepted. |
Change https://golang.org/cl/264460 mentions this issue: |
Currently many packages contain global vars with a default implementation that is used for package functions, e.g.
http.DefaultClient
andhttp.Get
. Internally, this is how thelog
package is structured with calls likelog.Print
forwarding tolog.std
, wherevar std = New(...)
.Can we export the
log.std
symbol? This could be useful for globally configuring a custom logger, or simply passing around the default Logger without needing to callNew(os.Stderr, "", LstdFlags)
and hoping that still matches up with the stdlib implementation.Proposed implementation
The text was updated successfully, but these errors were encountered: