docstore/vendor/github.com/go-logr/zerologr/zerologr.go
2022-09-19 22:52:40 +02:00

166 lines
4.7 KiB
Go

// Package zerologr defines an implementation of the github.com/go-logr/logr
// interfaces built on top of Zerolog (https://github.com/rs/zerolog).
//
// Usage
//
// A new logr.Logger can be constructed from an existing zerolog.Logger using
// the New function:
//
// log := zerologr.New(someZeroLogger)
//
// Implementation Details
//
// For the most part, concepts in Zerolog correspond directly with those in
// logr.
//
// Levels in logr correspond to custom debug levels in Zerolog. Any given level
// in logr is represents by `zerologLevel = 1 - logrLevel`.
// For example V(2) is equivalent to Zerolog's TraceLevel, while V(1) is
// equivalent to Zerolog's DebugLevel. Verbosity value is a number and is only
// logged on Info(), not Error().
package zerologr
import (
"fmt"
"github.com/go-logr/logr"
"github.com/rs/zerolog"
)
var (
// NameFieldName is the field key for logr.WithName.
NameFieldName = "logger"
// NameSeparator separates names for logr.WithName.
NameSeparator = "/"
// VerbosityFieldName is the field key for logr.Info verbosity. If set to "",
// its value is not emitted.
VerbosityFieldName = "v"
// RenderArgsHook mutates the list of key-value pairs passed directly to
// logr.Info and logr.Error.
RenderArgsHook = DefaultRender
// RenderValuesHook mutates the list of key-value pairs saved via logr.WithValues.
RenderValuesHook = DefaultRender
)
// Logger is type alias of logr.Logger.
type Logger = logr.Logger
// LogSink implements logr.LogSink and logr.CallDepthLogSink.
type LogSink struct {
l *zerolog.Logger
name string
depth int
}
// Underlier exposes access to the underlying logging implementation. Since
// callers only have a logr.Logger, they have to know which implementation is
// in use, so this interface is less of an abstraction and more of way to test
// type conversion.
type Underlier interface {
GetUnderlying() *zerolog.Logger
}
var (
_ logr.LogSink = &LogSink{}
_ logr.CallDepthLogSink = &LogSink{}
)
// New returns a logr.Logger with logr.LogSink implemented by Zerolog.
func New(l *zerolog.Logger) Logger {
ls := NewLogSink(l)
return logr.New(ls)
}
// NewLogSink returns a logr.LogSink implemented by Zerolog.
func NewLogSink(l *zerolog.Logger) *LogSink {
return &LogSink{l: l}
}
// Init receives runtime info about the logr library.
func (ls *LogSink) Init(ri logr.RuntimeInfo) {
ls.depth = ri.CallDepth + 2
}
// Enabled tests whether this LogSink is enabled at the specified V-level.
func (ls *LogSink) Enabled(level int) bool {
// Optimization: Info() will check level internally.
const traceLevel = 1 - int(zerolog.TraceLevel)
return level <= traceLevel
}
// Info logs a non-error message at specified V-level with the given key/value pairs as context.
func (ls *LogSink) Info(level int, msg string, keysAndValues ...interface{}) {
e := ls.l.WithLevel(zerolog.Level(1 - level))
if VerbosityFieldName != "" {
e.Int(VerbosityFieldName, level)
}
ls.msg(e, msg, keysAndValues)
}
// Error logs an error, with the given message and key/value pairs as context.
func (ls *LogSink) Error(err error, msg string, keysAndValues ...interface{}) {
e := ls.l.Error().Err(err)
ls.msg(e, msg, keysAndValues)
}
func (ls *LogSink) msg(e *zerolog.Event, msg string, keysAndValues []interface{}) {
if e == nil {
return
}
if ls.name != "" {
e.Str(NameFieldName, ls.name)
}
if RenderArgsHook != nil {
keysAndValues = RenderArgsHook(keysAndValues)
}
e = e.Fields(keysAndValues)
e.CallerSkipFrame(ls.depth)
e.Msg(msg)
}
// WithValues returns a new LogSink with additional key/value pairs.
func (ls LogSink) WithValues(keysAndValues ...interface{}) logr.LogSink {
if RenderValuesHook != nil {
keysAndValues = RenderValuesHook(keysAndValues)
}
l := ls.l.With().Fields(keysAndValues).Logger()
ls.l = &l
return &ls
}
// WithName returns a new LogSink with the specified name appended in NameFieldName.
// Name elements are separated by NameSeparator.
func (ls LogSink) WithName(name string) logr.LogSink {
if ls.name != "" {
ls.name += NameSeparator + name
} else {
ls.name = name
}
return &ls
}
// WithCallDepth returns a new LogSink that offsets the call stack by adding specified depths.
func (ls LogSink) WithCallDepth(depth int) logr.LogSink {
ls.depth += depth
return &ls
}
// GetUnderlying returns the zerolog.Logger underneath this logSink.
func (ls *LogSink) GetUnderlying() *zerolog.Logger {
return ls.l
}
// DefaultRender supports logr.Marshaler and fmt.Stringer.
func DefaultRender(keysAndValues []interface{}) []interface{} {
for i, n := 1, len(keysAndValues); i < n; i += 2 {
value := keysAndValues[i]
switch v := value.(type) {
case logr.Marshaler:
keysAndValues[i] = v.MarshalLog()
case fmt.Stringer:
keysAndValues[i] = v.String()
}
}
return keysAndValues
}