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

Getting level from zap logger #1144

Closed
krupyansky opened this issue Aug 5, 2022 · 5 comments · Fixed by #1148
Closed

Getting level from zap logger #1144

krupyansky opened this issue Aug 5, 2022 · 5 comments · Fixed by #1148

Comments

@krupyansky
Copy link

krupyansky commented Aug 5, 2022

How can I get log level from zap logger?

For logrus, this problem is solved in the following way:
https://pkg.go.dev/github.com/sirupsen/logrus#Logger.GetLevel
https://pkg.go.dev/github.com/sirupsen/logrus#Entry

I need a similar feature from zap logger.

@abhinav
Copy link
Collaborator

abhinav commented Aug 5, 2022

Hey, @krupyansky, Zap does not currently have a way of accessing the log level post-construction.
This is a problem we should solve, and we're going to try to think of a way around that, but meanwhile, there may be a workaround.

Workaround:

Construct your logger by supplying a zap.AtomicLevel.

If you're using zap.NewProduction or zap.NewDevelopment, you can do this:

atomLevel := zap.NewAtomicLevelAt(zap.InfoLevel)
encoderCfg := zap.NewProductionEncoderConfig()
logger := zap.New(zapcore.NewCore(
    zapcore.NewJSONEncoder(encoderCfg),
    zapcore.Lock(os.Stdout),
    atomLevel,
))

Following that, your atomLevel will always report the current log level.

fmt.Println(atomLevel.Level())
// atomLevel will report the logger's current level.

If you're using zap.Config to load your configuration, you can already access the AtomicLevel it uses for the logger at Config.Level.

var cfg zap.Config
// ...
logger, err := cfg.Build()
// ...

atomLevel := cfg.Level
fmt.Println(atomLevel.Level())
// atomLevel will report the logger's current level.

Solution

To actually solve this problem in Zap, we need to expose a means of reading the current log level.
One option is something similar to what you proposed in #1143, @krupyansky, but in a backwards compatible manner.

What if the Level() Level method was an optional upcast of zapcore.Core?
We can add a zapcore.LevelOf function that reports the level of a core if it implements that method.
Something roughly like,

package zapcore

// LevelOf reports the minimum enabled log level in the given Core.
// If the Core implements a "Level() Level" method, LevelOf delegates to it.
func LevelOf(core Core) Level {
  leveler, ok := core.(interface{ Level() Level })
  if ok { return leveler.Level() }

  // ...
}

If the Core does not implement the method, LevelOf can make a reasonable guess of the Level by looping through all levels in-order and calling Core.Enabled on them.

We can also add a Logger.Level() method based on this function to zap.Logger and zap.SugaredLogger.

What do you think, @mway @sywhang @prashantv?

@prashantv
Copy link
Collaborator

That seems reasonable to me. Looping over Enabled seems ok; though the interface technically allows something like Enabled(Info) && !Enabled(Warn). That said, I think returning the first enabled is a reasonable guess.

@krupyansky
Copy link
Author

@prashantv

Now, if I want to get level from zap, I need use this code:

func GetLevel(core zapcore.Core) zapcore.Level {
  if core.Enabled(zapcore.DebugLevel) {
    return zapcore.DebugLevel
  }
  if core.Enabled(zapcore.InfoLevel) {
    return zapcore.InfoLevel
  }
  if core.Enabled(zapcore.WarnLevel) {
    return zapcore.WarnLevel
  }
  if core.Enabled(zapcore.ErrorLevel) {
    return zapcore.ErrorLevel
  }
  if core.Enabled(zapcore.DPanicLevel) {
    return zapcore.DPanicLevel
  }
  if core.Enabled(zapcore.PanicLevel) {
    return zapcore.PanicLevel
  }
  if core.Enabled(zapcore.FatalLevel) {
    return zapcore.FatalLevel
  }
  return zapcore.DebugLevel
}

@abhinav

This construction

leveler, ok := core.(interface{ Level() Level })
if ok { return leveler.Level() }

not help me.

Inheritance is not work for the case.

abhinav added a commit that referenced this issue Aug 15, 2022
Add a new function LevelOf that reports the minimum enabled log level
for a LevelEnabler.

This works by looping through all known Zap levels in-order,
returning the newly introduced UnknownLevel if none of the known levels
are enabled.

A LevelEnabler or Core implementation may implement the `Level() Level`
method to override and optimize this behavior.
AtomicLevel already implemented this method.
This change adds the method to all Core implementations shipped with
Zap.

Note:
UnknownLevel is set at FatalLevel+1 to account for the possibility that
users of Zap are using DebugLevel-1 as their own custom log level--even
though this isn't supported, it's preferable not to break these users.
Users are less likely to use FatalLevel+1 since Fatal means "exit the
application."

Resolves #1144
Supersedes #1143, which was not backwards compatible
@abhinav
Copy link
Collaborator

abhinav commented Aug 15, 2022

@krupyansky
The suggestion was not to use the core.(interface{ Level() Level }) as-is, but as part of a new LevelOf function in Zap.
The way it would work is:

  • if the Core implements that interface, use it
  • Otherwise loop through the levels like in your code sample
  • All Core implementations shipped in Zap will implement the method

This way, when possible, we'll use the Level() method, but otherwise we'll fall back to trying every level.

I've created #1147 implementing this.

abhinav added a commit that referenced this issue Aug 15, 2022
Add a `Level() Level` method on Logger and SugaredLogger
that reports the current minimum enabled log level for the logger.

This relies on the zapcore.LevelOf function added in #1147.

Resolves #1144
abhinav added a commit that referenced this issue Aug 15, 2022
Add a `Level() Level` method on Logger and SugaredLogger
that reports the current minimum enabled log level for the logger.

This relies on the zapcore.LevelOf function added in #1147.

Resolves #1144
abhinav added a commit that referenced this issue Aug 15, 2022
Add a `Level() Level` method on Logger and SugaredLogger
that reports the current minimum enabled log level for the logger.

This relies on the zapcore.LevelOf function added in #1147.

Resolves #1144
abhinav added a commit that referenced this issue Aug 16, 2022
Add a new function LevelOf that reports the minimum enabled log level
for a LevelEnabler.
This works by looping through all known Zap levels in-order,
returning the newly introduced UnknownLevel if none of the known levels
are enabled.
A LevelEnabler or Core implementation may implement the Level() Level
method to override and optimize this behavior.
AtomicLevel already implemented this method.
This change adds the method to all Core implementations shipped with
Zap.
Note:
UnknownLevel is set at FatalLevel+1 to account for the possibility that
users of Zap are using DebugLevel-1 as their own custom log level--even
though this isn't supported, it's preferable not to break these users.
Users are less likely to use FatalLevel+1 since Fatal means "exit the
application."
Refs #1144
Supersedes #1143, which was not backwards compatible
Refs GO-1586
abhinav added a commit that referenced this issue Aug 16, 2022
Add a `Level() Level` method on Logger and SugaredLogger
that reports the current minimum enabled log level for the logger.

This relies on the zapcore.LevelOf function added in #1147.

Resolves #1144
abhinav added a commit that referenced this issue Aug 24, 2022
Add a `Level() Level` method on Logger and SugaredLogger
that reports the current minimum enabled log level for the logger.

This relies on the zapcore.LevelOf function added in #1147.

Resolves #1144
@sywhang
Copy link
Contributor

sywhang commented Aug 25, 2022

v1.23.0 contains this feature - closing this as done!

@sywhang sywhang closed this as completed Aug 25, 2022
abhinav added a commit that referenced this issue Aug 30, 2022
Add a `Level() Level` method on Logger and SugaredLogger
that reports the current minimum enabled log level for the logger.

This relies on the zapcore.LevelOf function added in #1147.

Resolves #1144
Depends on #1147
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging a pull request may close this issue.

4 participants