-
Notifications
You must be signed in to change notification settings - Fork 1.4k
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: Add zap.WithLeveler(leveler ...zapcore.LevelEnabler) #763
Comments
As mentioned in the previous discussion, with the current APIs of This is because the logger interface is using the There are a couple of methods here that deal with the level:
The implementation of Now when we wrap this core, we cannot force it to change the level to log any higher logs. In fact, the core might have something like this:
We can't change this level -- the only thing we can do is to ignore the result of |
I understand your argument I just don't really see how it applies as a counter argument to the feature. Lets look at the simplest implementation possible done externally: // WithLevel sets the loggers leveler to the one given.
func WithLevel(level zapcore.LevelEnabler) zap.Option {
return zap.WrapCore(func(core zapcore.Core) zapcore.Core {
return &lvlCore{Core: core, l: level}
})
}
type lvlCore struct {
zapcore.Core
l zapcore.LevelEnabler
}
func (c *lvlCore) Enabled(zapcore.Level) bool { return false }
func (c *lvlCore) Check(ent zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry {
if c.l.Enabled(ent.Level) {
return ce.AddCore(ent, c)
}
return ce
} func ExampleWithLevel() {
lr := zap.NewExample()
defer lr.Sync()
lr.Debug("debug message - shown")
// Raise the log level by passing the current core and the maximum desired
// level. Useful for muting a particularly noisy region of code without affecting
// other sections of your application.
lr = lr.WithOptions(WithLevel(zapcore.ErrorLevel))
lr.Info("info message - hidden")
lr.Error("error message - shown")
// Lower the current log level by passing a single lower LevelEnabler. Useful for
// debugging a specific region of problematic code without the noise of your
// entire application.
lr = lr.WithOptions(WithLevel(zapcore.InfoLevel))
lr.Debug("debug message - hidden")
lr.Info("info message - shown")
// Output:
// {"level":"debug","msg":"debug message - shown"}
// {"level":"error","msg":"error message - shown"}
// {"level":"info","msg":"info message - shown"}
} What specifically about the above is a violation of the Core?
Now the implementation details of the current sampler implemented by zap being ignored is a fair point, but you have lots of options. For example change the implementation slightly:
Which aligns with my objective:
There are other options with various degrees of reliability, optional interfaces, walking parent Cores and checking for *sampler and reusing the same internals and so on. I admit none of the options are "great" that try to accommodate Check calls of existing cores due to the convention that Check calls their own leveler. If it was a caller burden the problem wouldn't exist and contextual levels would be lightweight and simple. Ship has sailed though, I get it. That all said the implementation is not a problem in my eyes, it's a matter of should users be able to have scoped log levels as they do with fields, name etc. I think yes- as it was a surprise there was no way to do this for me given the flexibility of zap. If the zap maintainers disagree about the merit of the feature- then fair enough I can tuck it into a zaputil package and call it a day - it will be here for anyone else who wants it in a small copy paste. |
This feature can be made to work if we assume that the only implementations of This is about API guarantees, and the current API guarantee of a core is that the If you want to have a custom package that supports increasing levels, I would suggest add a note that any filtering in underlying cores may not be respected. Please do link to any external package you make, as it will help others looking for the same feature. Closing the issue since there's no zap work. |
Hi @prashantv, If I understand you correctly, the issue is that the way the Core decides whether an Entry should be written or not (in Check) can be completely arbitrary. In other words, Check may even ignore the Level of the Entry completely and return The core functionality of Core seems to be Check. As per the doc comment:
"Using the embedded LevelEnabler" suggests that the notion of Entries having a Level and Core only allowing Entries with certain Levels to be logged is inherent to the Core. For example, I understand this to mean that So there are two orthogonal concepts here:
I suppose that Level-based filtering can mean that we disregard the Entries' Level completely and always return In order to support the requested feature, it seems Core itself could support changing of the Level - since the notion of there being a Level is inherent to the design of the interface. The Zap cores for example take the Level at which they log as one of the constructor parameters. For example, In generality, the changing of the log level may fail, as for some Core implementations, it probably needn't make sense to try to change the log level. I cannot think of a practical example, but it's legal. So the operation should be allowed to fail (return an error). Since you probably don't want to introduce BC breaks by adding another method in Core's method set, we could leverage duck typing and provide a package-level function in zapcore to change the log level of a Core (for example Do you think there's a way forward here? |
Discussion on this in #591 (comment) with the use case. I would be happy to create a pull request for it.
Declaration:
Example (WithLeveler)
The text was updated successfully, but these errors were encountered: