-
-
Notifications
You must be signed in to change notification settings - Fork 2.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
kit/log/levels: support filtering #269
Comments
Dynamic inspection of log level with every Log invocation imposes a significant performance cost that we (ideally) want to avoid, or (at worst) make it explicitly opt-in. Let me propose this alternative to the existing log/levels package, which includes a concept of numeric level. type Level int
var (
LevelNone Level = iota
LevelError
LevelInfo
LevelDebug
)
type Levels struct {
Error log.Logger
Info log.Logger
Debug log.Logger
}
func NewLevels(logger log.Logger, level Level) Levels {
levels := Levels{
Error: log.NewNopLogger(),
Info: log.NewNopLogger(),
Debug: log.NewNopLogger(),
)
if level >= LevelError {
levels.Error = log.NewContext(logger).With("level", "error")
}
if level >= LevelInfo {
levels.Info = log.NewContext(logger).With("level", "info")
}
if level >= LevelDebug {
levels.Debug = log.NewContext(logger).With("level", "debug")
}
return levels
}
func Example() {
logger := log.NewLogfmtLogger(os.Stdout)
levels := levels.New(logger, LevelInfo)
levels.Debug.Log("msg", "this will NOT get printed")
levels.Info.Log("msg", "this will get printed")
levels.Error.Log("msg", "this will also get printed")
} AlternativeIt is also possible to opt-in to paying a runtime cost to get a slightly easier API. func NewKeyBasedLeveledLogger(logger log.Logger, level Level) log.Logger {
return leveledLogger{logger, level}
}
type leveledLogger struct {
logger log.Logger
level Level
}
func (l leveledLogger) Log(keyvals ...interface{}) {
for i := 0; i<len(keyvals); i += 2 {
// Pseudocode
k, v := keyvals[i], keyvals[i+1]
if k != "level" {
continue
}
if parseLevel(v) > l.level {
return // don't log this one
}
break
}
l.logger.Log(keyvals...)
}
func Example() {
var logger log.Logger
logger = log.NewLogfmtLogger(os.Stdout)
logger = NewKeyBasedLevelLogger(logger, LevelInfo)
logger.Log("level", "debug", "msg", "this is NOT printed")
logger.Log("level", "info", "msg", "this is printed")
logger.Log("level", "error", "msg", "this is also printed")
logger.Log("msg", "without a level, this gets printed, but we can change that behavior")
} |
@csguy Also keen on your input here. I've seen a lot of interest in numeric levels lately... |
Following up to the discussion on Slack, @peterbourgon and I raised an option to return a Levels interface that extends the current Logger: type levels.Logger interface {
log.Logger
Debug(keyvals ...interface{})
Info(keyvals ...interface{})
Error(keyvals ...interface{})
} So instead of writing a long command to print log, you can use a shorthand: logger.Log("level", levels.Debug, "msg", "this is NOT printed") or logger.NewContext(logger).With("level", levels.Debug) |
Another idea is to cast the first option @peterbourgon raised(multiple loggers on init) implement the interface above. Then the default This idea will allow us to:
@peterbourgon raised an idea to add an option |
There are a lot of ideas mentioned above. Here are my general comments: I think @peterbourgon is overestimating the relative cost of iterating over the
Adding a filtering layer as in @AlmogBaku's initial proposal and @peterbourgon's alternative proposal does not cost much more than directly calling As far as I know it is impossible to eliminate the second cost without wrapping every call to The benefit of using a filtering layer lies in how nicely it fits with the philosophy of For further reading about why the In my opinion, we should focus on a good API for a new decorator that filters log events containing specific levels. I think this means iterating on @peterbourgon's alternate approach and possibly changing the level values from strings to ints that format as strings if the log event reaches the output layer. |
Thanks very much, Chris. I will update the log/README.md with more context, and (especially) those requirements for new loggers, which seem to slip my mind whenever we have these discussions. |
@ChrisHines I'm not sure I understood why do you think(or do you?) that the IMO, We can have a common |
@AlmogBaku I am not sure I have completely understood your idea for a So, it feels to me like we've been down this path before, but as I said maybe I am missing an important difference in your idea. If you believe it can work then I encourage you to give it a try. Fork the project, experiment with the idea, see how it feels, submit a PR back to us and I will gladly review it. If your idea works then Go kit gets better; and even if it doesn't, we will have learned something more about the problem. |
Ok, so just to recap my understandings:
|
@AlmogBaku That seems like a reasonable summary, although it seems to me that (1) makes (2) unnecessary, so I don't understand what workaround is required. |
@AlmogBaku Do you think the recently rewritten and merged log/level package satisfies this issue? Can we close this? |
looks good |
Intro
I believe for some scenarios it's a good practice to "throw" a lot of logs(i.e. debugging/info), and in case of need to "expose them" in order to track of bugs.
However, you doesn't necessary want to send all those bugs to the "external" logs management(i.e. debug a single connection details, especially for real-time use cases). Mechanism of filtering by log-levels can be very useful for this scenarios.
Suggestion
Adding "just a layer of filtering" to the current structure is feasible, however- it will be based on string comparisons instead of mathematical comparisons (from DEBUG logs and above)
In order to achieve a verbosity level, I suggest to use a new type
type LogLevel int
, and to compare base on it.Example: https://gist.github.com/AlmogBaku/61f183350e10173377f85ee940a84741
If it make sense, i'd love to work on a PR to the existing kit/log/levels API, that contains this enhancement.
The text was updated successfully, but these errors were encountered: