mirror of
https://github.com/trufflesecurity/trufflehog.git
synced 2024-11-15 01:17:34 +00:00
115 lines
3.6 KiB
Go
115 lines
3.6 KiB
Go
|
package log
|
||
|
|
||
|
import (
|
||
|
"sort"
|
||
|
"sync"
|
||
|
|
||
|
"github.com/go-logr/logr"
|
||
|
"github.com/go-logr/zapr"
|
||
|
"go.uber.org/zap"
|
||
|
"go.uber.org/zap/zapcore"
|
||
|
)
|
||
|
|
||
|
// TODO: Use a struct to make testing easier.
|
||
|
var (
|
||
|
// Global, default log level control.
|
||
|
globalLogLevel levelSetter = zap.NewAtomicLevel()
|
||
|
|
||
|
// Map of name -> level control for independently setting log levels. A new
|
||
|
// control is registered via WithNamedLevel. This map is never cleaned up
|
||
|
// and new entries will overwrite previous values. Currently, this is
|
||
|
// acceptable behavior because WithNamedLevel is used sparingly.
|
||
|
globalControls map[string]levelSetter = make(map[string]levelSetter, 16)
|
||
|
// globalControls is protected (both read and write) by a mutex to make it
|
||
|
// thread safe. Access is low frequency, so performance is not a concern.
|
||
|
globalControlsLock sync.Mutex
|
||
|
)
|
||
|
|
||
|
type levelSetter interface {
|
||
|
zapcore.LevelEnabler
|
||
|
SetLevel(zapcore.Level)
|
||
|
Level() zapcore.Level
|
||
|
}
|
||
|
|
||
|
// SetLevel sets the log level for loggers created with the default level
|
||
|
// controller.
|
||
|
func SetLevel(level int8) {
|
||
|
SetLevelForControl(globalLogLevel, level)
|
||
|
}
|
||
|
|
||
|
// SetLevelForControl sets the log level for a given control.
|
||
|
func SetLevelForControl(control levelSetter, level int8) {
|
||
|
// Zap's levels get more verbose as the number gets smaller, as explained
|
||
|
// by zapr here: https://github.com/go-logr/zapr#increasing-verbosity
|
||
|
// For example setting the level to -2 below, means log.V(2) will be enabled.
|
||
|
control.SetLevel(zapcore.Level(-level))
|
||
|
}
|
||
|
|
||
|
// SetLevelFor sets the log level for a given named control.
|
||
|
func SetLevelFor(name string, level int8) {
|
||
|
globalControlsLock.Lock()
|
||
|
defer globalControlsLock.Unlock()
|
||
|
if control, ok := globalControls[name]; ok {
|
||
|
SetLevelForControl(control, level)
|
||
|
return
|
||
|
}
|
||
|
// Create a new control so registering a control with the same name will
|
||
|
// inherit the existing level.
|
||
|
globalControls[name] = newAtomicLevelAt(level)
|
||
|
}
|
||
|
|
||
|
// AddLeveler adds a log level control to a logr.Logger.
|
||
|
func AddLeveler(l logr.Logger, control levelSetter) (logr.Logger, error) {
|
||
|
zapLogger, err := getZapLogger(l)
|
||
|
if err != nil {
|
||
|
return l, err
|
||
|
}
|
||
|
|
||
|
zapLogger = zapLogger.WithOptions(zap.WrapCore(func(core zapcore.Core) zapcore.Core {
|
||
|
return NewLevelCore(core, control)
|
||
|
}))
|
||
|
return zapr.NewLogger(zapLogger), nil
|
||
|
}
|
||
|
|
||
|
// WithNamedLevel creates a child logger with a new name and independent log
|
||
|
// level control (see SetLevelFor). NOTE: if name already exists, the existing
|
||
|
// controller will be used, otherwise a new controller is created with level
|
||
|
// matching the parent's log level.
|
||
|
func WithNamedLevel(logger logr.Logger, name string) logr.Logger {
|
||
|
logger = logger.WithName(name)
|
||
|
|
||
|
globalControlsLock.Lock()
|
||
|
defer globalControlsLock.Unlock()
|
||
|
|
||
|
var leveler levelSetter
|
||
|
if currentControl, ok := globalControls[name]; ok {
|
||
|
leveler = currentControl
|
||
|
} else {
|
||
|
leveler = newAtomicLevelAt(findLevel(logger))
|
||
|
globalControls[name] = leveler
|
||
|
}
|
||
|
newLogger, err := AddLeveler(logger, leveler)
|
||
|
if err != nil {
|
||
|
return logger
|
||
|
}
|
||
|
return newLogger
|
||
|
}
|
||
|
|
||
|
// newAtomicLevelAt is a helper function to create a zap.AtomicLevel
|
||
|
// initialized with a level. We cannot use zap.NewAtomicLevelAt here because of
|
||
|
// a quirk with logr levels (see SetLevelForControl).
|
||
|
func newAtomicLevelAt(level int8) zap.AtomicLevel {
|
||
|
control := zap.NewAtomicLevel()
|
||
|
SetLevelForControl(control, level)
|
||
|
return control
|
||
|
}
|
||
|
|
||
|
// findLevel probes a logr.Logger to figure out what level it is at via binary
|
||
|
// search. We only search [0, 128), so worst case is ~7 checks.
|
||
|
func findLevel(logger logr.Logger) int8 {
|
||
|
sink := logger.GetSink()
|
||
|
return int8(sort.Search(128, func(i int) bool {
|
||
|
return !sink.Enabled(i)
|
||
|
}) - 1)
|
||
|
}
|