2021-09-27 14:57:40 +00:00
|
|
|
package level
|
|
|
|
|
2022-05-20 11:45:24 +00:00
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"github.com/go-kit/log"
|
|
|
|
)
|
|
|
|
|
|
|
|
// ErrInvalidLevelString is returned whenever an invalid string is passed to Parse.
|
|
|
|
var ErrInvalidLevelString = errors.New("invalid level string")
|
2021-09-27 14:57:40 +00:00
|
|
|
|
|
|
|
// Error returns a logger that includes a Key/ErrorValue pair.
|
|
|
|
func Error(logger log.Logger) log.Logger {
|
|
|
|
return log.WithPrefix(logger, Key(), ErrorValue())
|
|
|
|
}
|
|
|
|
|
|
|
|
// Warn returns a logger that includes a Key/WarnValue pair.
|
|
|
|
func Warn(logger log.Logger) log.Logger {
|
|
|
|
return log.WithPrefix(logger, Key(), WarnValue())
|
|
|
|
}
|
|
|
|
|
|
|
|
// Info returns a logger that includes a Key/InfoValue pair.
|
|
|
|
func Info(logger log.Logger) log.Logger {
|
|
|
|
return log.WithPrefix(logger, Key(), InfoValue())
|
|
|
|
}
|
|
|
|
|
|
|
|
// Debug returns a logger that includes a Key/DebugValue pair.
|
|
|
|
func Debug(logger log.Logger) log.Logger {
|
|
|
|
return log.WithPrefix(logger, Key(), DebugValue())
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewFilter wraps next and implements level filtering. See the commentary on
|
|
|
|
// the Option functions for a detailed description of how to configure levels.
|
|
|
|
// If no options are provided, all leveled log events created with Debug,
|
|
|
|
// Info, Warn or Error helper methods are squelched and non-leveled log
|
|
|
|
// events are passed to next unmodified.
|
|
|
|
func NewFilter(next log.Logger, options ...Option) log.Logger {
|
|
|
|
l := &logger{
|
|
|
|
next: next,
|
|
|
|
}
|
|
|
|
for _, option := range options {
|
|
|
|
option(l)
|
|
|
|
}
|
|
|
|
return l
|
|
|
|
}
|
|
|
|
|
|
|
|
type logger struct {
|
|
|
|
next log.Logger
|
|
|
|
allowed level
|
|
|
|
squelchNoLevel bool
|
|
|
|
errNotAllowed error
|
|
|
|
errNoLevel error
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *logger) Log(keyvals ...interface{}) error {
|
|
|
|
var hasLevel, levelAllowed bool
|
|
|
|
for i := 1; i < len(keyvals); i += 2 {
|
|
|
|
if v, ok := keyvals[i].(*levelValue); ok {
|
|
|
|
hasLevel = true
|
|
|
|
levelAllowed = l.allowed&v.level != 0
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if !hasLevel && l.squelchNoLevel {
|
|
|
|
return l.errNoLevel
|
|
|
|
}
|
|
|
|
if hasLevel && !levelAllowed {
|
|
|
|
return l.errNotAllowed
|
|
|
|
}
|
|
|
|
return l.next.Log(keyvals...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Option sets a parameter for the leveled logger.
|
|
|
|
type Option func(*logger)
|
|
|
|
|
2022-05-20 11:45:24 +00:00
|
|
|
// Allow the provided log level to pass.
|
|
|
|
func Allow(v Value) Option {
|
|
|
|
switch v {
|
|
|
|
case debugValue:
|
|
|
|
return AllowDebug()
|
|
|
|
case infoValue:
|
|
|
|
return AllowInfo()
|
|
|
|
case warnValue:
|
|
|
|
return AllowWarn()
|
|
|
|
case errorValue:
|
|
|
|
return AllowError()
|
|
|
|
default:
|
|
|
|
return AllowNone()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-09-27 14:57:40 +00:00
|
|
|
// AllowAll is an alias for AllowDebug.
|
|
|
|
func AllowAll() Option {
|
|
|
|
return AllowDebug()
|
|
|
|
}
|
|
|
|
|
|
|
|
// AllowDebug allows error, warn, info and debug level log events to pass.
|
|
|
|
func AllowDebug() Option {
|
|
|
|
return allowed(levelError | levelWarn | levelInfo | levelDebug)
|
|
|
|
}
|
|
|
|
|
|
|
|
// AllowInfo allows error, warn and info level log events to pass.
|
|
|
|
func AllowInfo() Option {
|
|
|
|
return allowed(levelError | levelWarn | levelInfo)
|
|
|
|
}
|
|
|
|
|
|
|
|
// AllowWarn allows error and warn level log events to pass.
|
|
|
|
func AllowWarn() Option {
|
|
|
|
return allowed(levelError | levelWarn)
|
|
|
|
}
|
|
|
|
|
|
|
|
// AllowError allows only error level log events to pass.
|
|
|
|
func AllowError() Option {
|
|
|
|
return allowed(levelError)
|
|
|
|
}
|
|
|
|
|
|
|
|
// AllowNone allows no leveled log events to pass.
|
|
|
|
func AllowNone() Option {
|
|
|
|
return allowed(0)
|
|
|
|
}
|
|
|
|
|
|
|
|
func allowed(allowed level) Option {
|
|
|
|
return func(l *logger) { l.allowed = allowed }
|
|
|
|
}
|
|
|
|
|
2022-05-20 11:45:24 +00:00
|
|
|
// Parse a string to its corresponding level value. Valid strings are "debug",
|
|
|
|
// "info", "warn", and "error". Strings are normalized via strings.TrimSpace and
|
|
|
|
// strings.ToLower.
|
|
|
|
func Parse(level string) (Value, error) {
|
|
|
|
switch strings.TrimSpace(strings.ToLower(level)) {
|
|
|
|
case debugValue.name:
|
|
|
|
return debugValue, nil
|
|
|
|
case infoValue.name:
|
|
|
|
return infoValue, nil
|
|
|
|
case warnValue.name:
|
|
|
|
return warnValue, nil
|
|
|
|
case errorValue.name:
|
|
|
|
return errorValue, nil
|
|
|
|
default:
|
|
|
|
return nil, ErrInvalidLevelString
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// ParseDefault calls Parse and returns the default Value on error.
|
|
|
|
func ParseDefault(level string, def Value) Value {
|
|
|
|
v, err := Parse(level)
|
|
|
|
if err != nil {
|
|
|
|
return def
|
|
|
|
}
|
|
|
|
return v
|
|
|
|
}
|
|
|
|
|
2021-09-27 14:57:40 +00:00
|
|
|
// ErrNotAllowed sets the error to return from Log when it squelches a log
|
|
|
|
// event disallowed by the configured Allow[Level] option. By default,
|
|
|
|
// ErrNotAllowed is nil; in this case the log event is squelched with no
|
|
|
|
// error.
|
|
|
|
func ErrNotAllowed(err error) Option {
|
|
|
|
return func(l *logger) { l.errNotAllowed = err }
|
|
|
|
}
|
|
|
|
|
|
|
|
// SquelchNoLevel instructs Log to squelch log events with no level, so that
|
|
|
|
// they don't proceed through to the wrapped logger. If SquelchNoLevel is set
|
|
|
|
// to true and a log event is squelched in this way, the error value
|
|
|
|
// configured with ErrNoLevel is returned to the caller.
|
|
|
|
func SquelchNoLevel(squelch bool) Option {
|
|
|
|
return func(l *logger) { l.squelchNoLevel = squelch }
|
|
|
|
}
|
|
|
|
|
|
|
|
// ErrNoLevel sets the error to return from Log when it squelches a log event
|
|
|
|
// with no level. By default, ErrNoLevel is nil; in this case the log event is
|
|
|
|
// squelched with no error.
|
|
|
|
func ErrNoLevel(err error) Option {
|
|
|
|
return func(l *logger) { l.errNoLevel = err }
|
|
|
|
}
|
|
|
|
|
|
|
|
// NewInjector wraps next and returns a logger that adds a Key/level pair to
|
|
|
|
// the beginning of log events that don't already contain a level. In effect,
|
|
|
|
// this gives a default level to logs without a level.
|
|
|
|
func NewInjector(next log.Logger, level Value) log.Logger {
|
|
|
|
return &injector{
|
|
|
|
next: next,
|
|
|
|
level: level,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
type injector struct {
|
|
|
|
next log.Logger
|
|
|
|
level interface{}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *injector) Log(keyvals ...interface{}) error {
|
|
|
|
for i := 1; i < len(keyvals); i += 2 {
|
|
|
|
if _, ok := keyvals[i].(*levelValue); ok {
|
|
|
|
return l.next.Log(keyvals...)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
kvs := make([]interface{}, len(keyvals)+2)
|
|
|
|
kvs[0], kvs[1] = key, l.level
|
|
|
|
copy(kvs[2:], keyvals)
|
|
|
|
return l.next.Log(kvs...)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Value is the interface that each of the canonical level values implement.
|
|
|
|
// It contains unexported methods that prevent types from other packages from
|
|
|
|
// implementing it and guaranteeing that NewFilter can distinguish the levels
|
|
|
|
// defined in this package from all other values.
|
|
|
|
type Value interface {
|
|
|
|
String() string
|
|
|
|
levelVal()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Key returns the unique key added to log events by the loggers in this
|
|
|
|
// package.
|
|
|
|
func Key() interface{} { return key }
|
|
|
|
|
|
|
|
// ErrorValue returns the unique value added to log events by Error.
|
|
|
|
func ErrorValue() Value { return errorValue }
|
|
|
|
|
|
|
|
// WarnValue returns the unique value added to log events by Warn.
|
|
|
|
func WarnValue() Value { return warnValue }
|
|
|
|
|
|
|
|
// InfoValue returns the unique value added to log events by Info.
|
|
|
|
func InfoValue() Value { return infoValue }
|
|
|
|
|
|
|
|
// DebugValue returns the unique value added to log events by Debug.
|
|
|
|
func DebugValue() Value { return debugValue }
|
|
|
|
|
|
|
|
var (
|
|
|
|
// key is of type interface{} so that it allocates once during package
|
|
|
|
// initialization and avoids allocating every time the value is added to a
|
|
|
|
// []interface{} later.
|
|
|
|
key interface{} = "level"
|
|
|
|
|
|
|
|
errorValue = &levelValue{level: levelError, name: "error"}
|
|
|
|
warnValue = &levelValue{level: levelWarn, name: "warn"}
|
|
|
|
infoValue = &levelValue{level: levelInfo, name: "info"}
|
|
|
|
debugValue = &levelValue{level: levelDebug, name: "debug"}
|
|
|
|
)
|
|
|
|
|
|
|
|
type level byte
|
|
|
|
|
|
|
|
const (
|
|
|
|
levelDebug level = 1 << iota
|
|
|
|
levelInfo
|
|
|
|
levelWarn
|
|
|
|
levelError
|
|
|
|
)
|
|
|
|
|
|
|
|
type levelValue struct {
|
|
|
|
name string
|
|
|
|
level
|
|
|
|
}
|
|
|
|
|
|
|
|
func (v *levelValue) String() string { return v.name }
|
|
|
|
func (v *levelValue) levelVal() {}
|