Skip to content

Commit

Permalink
Merge d985d4b into 2b2038c
Browse files Browse the repository at this point in the history
  • Loading branch information
bombsimon committed May 31, 2021
2 parents 2b2038c + d985d4b commit c49a36a
Show file tree
Hide file tree
Showing 6 changed files with 145 additions and 71 deletions.
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ import (
)

func main() {
var log logr.Logger

log = logrusr.NewLogger(logrus.New())
logrusLog := logrus.New()
log := logrusr.NewLogger(logrusLog)

log = log.WithName("MyName").WithValues("user", "you")
log.Info("Logr in action!", "the answer", 42)
}
```
Expand Down
23 changes: 16 additions & 7 deletions example/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,13 @@ import (
"fmt"

"github.com/bombsimon/logrusr"
"github.com/go-logr/logr"
"github.com/sirupsen/logrus"
)

func main() {
var (
log logr.Logger
)

logrusLog := logrus.New()
log := logrusr.New(logrusLog)

log = logrusr.NewLogger(logrusLog)
log = log.WithName("MyName").WithValues("user", "you")
log.Info("hello", "val1", 1, "val2", map[string]int{"k": 1})
log.V(0).Info("you should see this")
Expand All @@ -30,15 +25,29 @@ func main() {
"some_field": "some_value",
"another_field": 42,
})
log = logrusr.New(entryLog)

log = logrusr.NewLogger(entryLog)
log = log.WithName("MyName").WithValues("user", "you")
log.Info("hello", "val1", 1, "val2", map[string]int{"k": 1})
log.V(0).Info("you should see this")
log.V(2).Info("you should NOT see this")
log.Error(nil, "uh oh", "trouble", true, "reasons", []float64{0.1, 0.11, 3.14})
log.Error(errors.New("caught error"), "goodbye", "code", -1)

fmt.Println("")

log = log.WithName("subpackage")
log.Info("hello from subpackage")
log.WithName("even_deeper").Info("hello even deeper")

fmt.Println("")

logrusLog = logrus.New()
logrusLog.SetLevel(logrus.TraceLevel)

log = logrusr.New(
logrusLog,
logrusr.WithReportCaller(),
).WithCallDepth(0)
log.V(2).Info("NOW you should see this")
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/bombsimon/logrusr
go 1.13

require (
github.com/go-logr/logr v0.1.0
github.com/go-logr/logr v1.0.0-rc1
github.com/konsorten/go-windows-terminal-sequences v1.0.2 // indirect
github.com/kr/pretty v0.2.0 // indirect
github.com/sirupsen/logrus v1.4.2
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-logr/logr v0.1.0 h1:M1Tv3VzNlEHg6uyACnRdtrploV2P7wZqH8BoQMtz0cg=
github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas=
github.com/go-logr/logr v1.0.0-rc1 h1:+ul9F74rBkPajeP8m4o3o0tiglmzNFsPnuhYyBCQ0Sc=
github.com/go-logr/logr v1.0.0-rc1/go.mod h1:z6/tIYblkpsD+a4lm/fGIIU9mZ+XfAiaFtq7xTgseGU=
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/konsorten/go-windows-terminal-sequences v1.0.2 h1:DB17ag19krx9CFsz4o3enTrPXyIXCl+2iCXH/aMAp9s=
Expand Down
181 changes: 122 additions & 59 deletions logrusr.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ package logrusr

import (
"encoding/json"
"fmt"
"path/filepath"
"runtime"
"strings"

"github.com/go-logr/logr"
Expand All @@ -12,42 +15,73 @@ import (
// 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
)
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
}
}

type logrusr struct {
name []string
level int
depth int
reportCaller bool
logger logrus.FieldLogger
defaultFormatter func(interface{}) string
defaultFormatter FormatFunc
}

// NewLogger will return a new logr.Logger from a logrus.FieldLogger.
func NewLogger(l logrus.FieldLogger, name ...string) logr.Logger {
return NewLoggerWithFormatter(l, nil, name...)
}
// New will return a new logr.Logger created from a logrus.FieldLogger.
func New(l logrus.FieldLogger, opts ...Option) logr.Logger {
logger := &logrusr{
depth: 0,
logger: l,
}

// NewLoggerWithFormatter will return a new logr.Logger from a
// logrus.FieldLogger that uses provided function to format complex data types.
func NewLoggerWithFormatter(l logrus.FieldLogger, formatter func(interface{}) string, name ...string) logr.Logger {
return &logrusr{
name: name,
level: 0,
logger: l,
defaultFormatter: formatter,
for _, o := range opts {
o(logger)
}

return logr.New(logger)
}

// Enabled is a part of the InfoLogger interface. It will return true if the
// logrus.Logger has a level set to logrus.InfoLevel or higher (Debug/Trace).
// Init receives optional information about the library.
func (l *logrusr) Init(ri logr.RuntimeInfo) {
l.depth = ri.CallDepth
}

// 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() bool {
func (l *logrusr) Enabled(level int) bool {
var log *logrus.Logger

switch t := l.logger.(type) {
Expand All @@ -61,23 +95,44 @@ func (l *logrusr) Enabled() 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 int(log.GetLevel())-logrusDiffToInfo >= l.level
return log.IsLevelEnabled(logrus.Level(level + logrusDiffToInfo))
}

// V is a part of the Logger interface. Calling the method will change the
// global log severity for the logr implementation.
func (l *logrusr) V(level int) logr.InfoLogger {
newLogger := l.copyLogger()
newLogger.level = level
// 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{}) {
if !l.Enabled(level) {
return
}

return newLogger
if c := l.caller(); c != "" {
l.logger = l.logger.WithField("caller", c)
}

l.logger.
WithFields(listToLogrusFields(l.defaultFormatter, keysAndValues...)).
Info(msg)
}

// WithValues is a part of the Logger interface. 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.Logger {
// 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{}) {
if c := l.caller(); c != "" {
l.logger = l.logger.WithField("caller", c)
}

l.logger.
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 = l.logger.WithFields(
listToLogrusFields(l.defaultFormatter, keysAndValues...),
Expand All @@ -88,7 +143,7 @@ func (l *logrusr) WithValues(keysAndValues ...interface{}) logr.Logger {

// 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.Logger {
func (l *logrusr) WithName(name string) logr.LogSink {
newLogger := l.copyLogger()
newLogger.name = append(newLogger.name, name)

Expand All @@ -99,33 +154,11 @@ func (l *logrusr) WithName(name string) logr.Logger {
return newLogger
}

// 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(msg string, keysAndValues ...interface{}) {
if !l.Enabled() {
return
}

l.logger.
WithFields(listToLogrusFields(l.defaultFormatter, keysAndValues...)).
Info(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{}) {
l.logger.
WithFields(listToLogrusFields(l.defaultFormatter, keysAndValues...)).
WithError(err).
Error(msg)
}

// listToLogrusFields converts a list of arbitrary length to key/value paris.
func listToLogrusFields(formatter func(interface{}) string, keysAndValues ...interface{}) logrus.Fields {
var f = logrus.Fields{}
f := make(logrus.Fields)

// Skip all fields if it's not an even lengthed list.
// Skip all fields if it's not an even length list.
if len(keysAndValues)%2 != 0 {
return f
}
Expand Down Expand Up @@ -159,15 +192,45 @@ func listToLogrusFields(formatter func(interface{}) string, keysAndValues ...int
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)),
level: l.level,
defaultFormatter: l.defaultFormatter,
depth: l.depth,
reportCaller: l.reportCaller,
logger: l.logger,
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)
}
2 changes: 1 addition & 1 deletion logrusr_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ func TestLogging(t *testing.T) {

// Send the created logger to the test case to invoke desired
// logging.
tc.logFunc(NewLoggerWithFormatter(logrusLogger, tc.formatter))
tc.logFunc(New(logrusLogger, WithFormatter(tc.formatter)))

if tc.assertions == nil {
assert.Equal(t, logWriter.Len(), 0)
Expand Down

0 comments on commit c49a36a

Please sign in to comment.