Initial QSfera import
This commit is contained in:
+237
@@ -0,0 +1,237 @@
|
||||
package logrusr
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// According to the specification of the Logger interface calling the InfoLogger
|
||||
// directly on the logger should be the same as calling them on V(0). Since
|
||||
// logrus level 0 is PanicLevel and Infolevel doesn't start until V(4) we use
|
||||
// this constant to be able to calculate what V(n) values should mean.
|
||||
const logrusDiffToInfo = 4
|
||||
|
||||
// FormatFunc is the function to format log values with for non primitive data.
|
||||
// By default, this is empty and the data will be JSON marshaled.
|
||||
type FormatFunc func(interface{}) string
|
||||
|
||||
// Option is options to give when construction a logrusr logger.
|
||||
type Option func(l *logrusr)
|
||||
|
||||
// WithFormatter will set the FormatFunc to use.
|
||||
func WithFormatter(f FormatFunc) Option {
|
||||
return func(l *logrusr) {
|
||||
l.defaultFormatter = f
|
||||
}
|
||||
}
|
||||
|
||||
// WithReportCaller will enable reporting of the caller.
|
||||
func WithReportCaller() Option {
|
||||
return func(l *logrusr) {
|
||||
l.reportCaller = true
|
||||
}
|
||||
}
|
||||
|
||||
// WithName will set an initial name instead of having to call `WithName` on the
|
||||
// logger itself after constructing it.
|
||||
func WithName(name ...string) Option {
|
||||
return func(l *logrusr) {
|
||||
l.name = name
|
||||
|
||||
l.logger = l.logger.WithField(
|
||||
"logger", strings.Join(l.name, "."),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
type logrusr struct {
|
||||
name []string
|
||||
depth int
|
||||
reportCaller bool
|
||||
logger *logrus.Entry
|
||||
defaultFormatter FormatFunc
|
||||
}
|
||||
|
||||
// New will return a new logr.Logger created from a logrus.FieldLogger.
|
||||
func New(l logrus.FieldLogger, opts ...Option) logr.Logger {
|
||||
// Immediately convert the FieldLogger to an Entry so we don't have to type
|
||||
// cast and can use methods that exist on the Entry but not the FieldLogger
|
||||
// interface.
|
||||
logger := &logrusr{
|
||||
depth: 0,
|
||||
logger: l.WithFields(logrus.Fields{}),
|
||||
}
|
||||
|
||||
for _, o := range opts {
|
||||
o(logger)
|
||||
}
|
||||
|
||||
return logr.New(logger)
|
||||
}
|
||||
|
||||
// Init receives optional information about the library.
|
||||
func (l *logrusr) Init(ri logr.RuntimeInfo) {
|
||||
// By default `CallDepth` is set to 1 which means one of the frames is
|
||||
// skipped by default. This was originally missed in this library making the
|
||||
// default behavior and `WithCallDepth(0)` behave differently.
|
||||
// To be backwards compatible without affecting anyone manually setting the
|
||||
// call depth we reduce 1 from the default depth instead of not adding it.
|
||||
// See https://github.com/bombsimon/logrusr/issues/19 for more info.
|
||||
l.depth = ri.CallDepth - 1
|
||||
}
|
||||
|
||||
// Enabled tests whether this Logger is enabled. It will return true if the
|
||||
// logrus.Logger has a level set to logrus.InfoLevel or higher (Warn/Panic).
|
||||
// According to the documentation, level V(0) should be equivalent as calling
|
||||
// Info() directly on the logger. To ensure this the constant `logrusDiffToInfo`
|
||||
// will be added to all passed values so that V(0) creates a logger with level
|
||||
// logrus.InfoLevel and V(2) would create a logger with level logrus.TraceLevel.
|
||||
// This menas that if logrus is set to logrus.InfoLevel or **higher** this
|
||||
// method will return true, otherwise false.
|
||||
func (l *logrusr) Enabled(level int) bool {
|
||||
// logrus.InfoLevel has value 4 so if the level on the logger is set to 0 we
|
||||
// should only be seen as enabled if the logrus logger has a severity of
|
||||
// info or higher.
|
||||
return l.logger.Logger.IsLevelEnabled(logrus.Level(level + logrusDiffToInfo))
|
||||
}
|
||||
|
||||
// Info logs info messages if the logger is enabled, that is if the level on the
|
||||
// logger is set to logrus.InfoLevel or less.
|
||||
func (l *logrusr) Info(level int, msg string, keysAndValues ...interface{}) {
|
||||
log := l.logger
|
||||
if c := l.caller(); c != "" {
|
||||
log = log.WithField("caller", c)
|
||||
}
|
||||
|
||||
log.
|
||||
WithFields(listToLogrusFields(l.defaultFormatter, keysAndValues...)).
|
||||
Log(logrus.Level(level+logrusDiffToInfo), msg)
|
||||
}
|
||||
|
||||
// Error logs error messages. Since the log will be written with `Error` level
|
||||
// it won't show if the severity of the underlying logrus logger is less than
|
||||
// Error.
|
||||
func (l *logrusr) Error(err error, msg string, keysAndValues ...interface{}) {
|
||||
log := l.logger
|
||||
if c := l.caller(); c != "" {
|
||||
log = log.WithField("caller", c)
|
||||
}
|
||||
|
||||
log.
|
||||
WithFields(listToLogrusFields(l.defaultFormatter, keysAndValues...)).
|
||||
WithError(err).
|
||||
Error(msg)
|
||||
}
|
||||
|
||||
// WithValues returns a new logger with additional key/values pairs. This is
|
||||
// equivalent to logrus WithFields() but takes a list of even arguments
|
||||
// (key/value pairs) instead of a map as input. If an odd number of arguments
|
||||
// are sent all values will be discarded.
|
||||
func (l *logrusr) WithValues(keysAndValues ...interface{}) logr.LogSink {
|
||||
newLogger := l.copyLogger()
|
||||
newLogger.logger = newLogger.logger.WithFields(
|
||||
listToLogrusFields(l.defaultFormatter, keysAndValues...),
|
||||
)
|
||||
|
||||
return newLogger
|
||||
}
|
||||
|
||||
// WithName is a part of the Logger interface. This will set the key "logger" as
|
||||
// a logrus field to identify the instance.
|
||||
func (l *logrusr) WithName(name string) logr.LogSink {
|
||||
newLogger := l.copyLogger()
|
||||
newLogger.name = append(newLogger.name, name)
|
||||
|
||||
newLogger.logger = newLogger.logger.WithField(
|
||||
"logger", strings.Join(newLogger.name, "."),
|
||||
)
|
||||
|
||||
return newLogger
|
||||
}
|
||||
|
||||
// listToLogrusFields converts a list of arbitrary length to key/value paris.
|
||||
func listToLogrusFields(formatter func(interface{}) string, keysAndValues ...interface{}) logrus.Fields {
|
||||
f := make(logrus.Fields)
|
||||
|
||||
// Skip all fields if it's not an even length list.
|
||||
if len(keysAndValues)%2 != 0 {
|
||||
return f
|
||||
}
|
||||
|
||||
for i := 0; i < len(keysAndValues); i += 2 {
|
||||
k, v := keysAndValues[i], keysAndValues[i+1]
|
||||
|
||||
if s, ok := k.(string); ok {
|
||||
// Try to avoid marshaling known types.
|
||||
switch vVal := v.(type) {
|
||||
case int, int8, int16, int32, int64,
|
||||
uint, uint8, uint16, uint32, uint64,
|
||||
float32, float64, complex64, complex128,
|
||||
string, bool:
|
||||
f[s] = vVal
|
||||
|
||||
case []byte:
|
||||
f[s] = string(vVal)
|
||||
|
||||
default:
|
||||
if formatter != nil {
|
||||
f[s] = formatter(v)
|
||||
} else {
|
||||
j, _ := json.Marshal(vVal)
|
||||
f[s] = string(j)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return f
|
||||
}
|
||||
|
||||
// copyLogger copies the logger creating a new slice of the name but preserving
|
||||
// the formatter and actual logrus logger.
|
||||
func (l *logrusr) copyLogger() *logrusr {
|
||||
newLogger := &logrusr{
|
||||
name: make([]string, len(l.name)),
|
||||
depth: l.depth,
|
||||
reportCaller: l.reportCaller,
|
||||
logger: l.logger.Dup(),
|
||||
defaultFormatter: l.defaultFormatter,
|
||||
}
|
||||
|
||||
copy(newLogger.name, l.name)
|
||||
|
||||
return newLogger
|
||||
}
|
||||
|
||||
// WithCallDepth implements the optional WithCallDepth to offset the call stack
|
||||
// when reporting caller.
|
||||
func (l *logrusr) WithCallDepth(depth int) logr.LogSink {
|
||||
newLogger := l.copyLogger()
|
||||
newLogger.depth = depth
|
||||
|
||||
return newLogger
|
||||
}
|
||||
|
||||
// caller will return the caller of the logging method.
|
||||
func (l *logrusr) caller() string {
|
||||
// Check if we should even report the caller.
|
||||
if !l.reportCaller {
|
||||
return ""
|
||||
}
|
||||
|
||||
// +1 for this frame.
|
||||
// +1 for frame calling here (Info/Error)
|
||||
// +1 for logr frame
|
||||
_, file, line, ok := runtime.Caller(l.depth + 3)
|
||||
if !ok {
|
||||
return ""
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s:%d", filepath.Base(file), line)
|
||||
}
|
||||
Reference in New Issue
Block a user