zwrap/wrap.go

325 lines
6.7 KiB
Go

// Package zwrap provides a wrapper for zerolog.Logger that implements the standard library's log.Logger methods,
// as well as other common logging methods as an attempt to provide compatibility with other logging libraries.
package zwrap
import (
"bytes"
"fmt"
"log"
"strings"
"sync"
"github.com/rs/zerolog"
)
var strBufs = &sync.Pool{
New: func() interface{} {
return new(strings.Builder)
},
}
// StdCompatLogger is an interface that provides compatibility with the standard library's log.Logger.
type StdCompatLogger interface {
Fatal(v ...interface{})
Fatalf(format string, v ...interface{})
Fatalln(v ...interface{})
Panic(v ...interface{})
Panicf(format string, v ...interface{})
Panicln(v ...interface{})
Prefix() string
Print(v ...interface{})
Printf(format string, v ...interface{})
Println(v ...interface{})
SetPrefix(prefix string)
Output(calldepth int, s string) error
}
// assert that Logger implements StdCompatLogger and that log.Logger implements StdCompatLogger
var _ StdCompatLogger = &Logger{}
var _ StdCompatLogger = &log.Logger{}
// ----------------------------------------------------
type Logger struct {
*zerolog.Logger
*sync.RWMutex
prefix string
printLevel zerolog.Level
}
func (l *Logger) SetPrefix(prefix string) {
l.Lock()
l.prefix = prefix
l.Unlock()
}
func (l *Logger) SetPrintLevel(level zerolog.Level) {
l.Lock()
l.printLevel = level
l.Unlock()
}
func (l *Logger) Prefix() string {
l.RLock()
defer l.RUnlock()
return l.prefix
}
func (l *Logger) Println(v ...interface{}) {
l.RLock()
l.Logger.WithLevel(l.printLevel).Msg(fmt.Sprint(v...))
l.RUnlock()
}
func (l *Logger) Printf(format string, v ...interface{}) {
l.RLock()
l.Logger.WithLevel(l.printLevel).Msgf(format, v...)
l.RUnlock()
}
func (l *Logger) Print(v ...interface{}) {
l.RLock()
l.Logger.WithLevel(l.printLevel).Msg(fmt.Sprint(v...))
l.RUnlock()
}
func (l *Logger) Fatal(v ...interface{}) {
// Don't check mutex here because we're exiting anyway.
printLn(l.Logger.Fatal(), v...)
}
func (l *Logger) Fatalf(format string, v ...interface{}) {
// Don't check mutex here because we're exiting anyway.
l.Logger.Fatal().Msgf(format, v...)
}
func (l *Logger) Fatalln(v ...interface{}) {
// Don't check mutex here because we're exiting anyway.
printLn(l.Logger.Fatal(), v...)
}
func (l *Logger) Panic(v ...interface{}) {
// Don't check mutex here because we're panicking anyway.
printLn(l.Logger.Panic(), v...)
}
func (l *Logger) Panicf(format string, v ...interface{}) {
// Don't check mutex here because we're panicking anyway.
l.Logger.Panic().Msgf(format, v...)
}
func (l *Logger) Panicln(v ...interface{}) {
// Don't check mutex here because we're panicking anyway.
printLn(l.Logger.Panic(), v...)
}
func (l *Logger) Errorf(format string, v ...interface{}) {
l.RLock()
l.Logger.Error().Msgf(format, v...)
l.RUnlock()
}
func (l *Logger) Warnf(format string, v ...interface{}) {
l.RLock()
l.Logger.Warn().Msgf(format, v...)
l.RUnlock()
}
func (l *Logger) Infof(format string, v ...interface{}) {
l.RLock()
l.Logger.Info().Msgf(format, v...)
l.RUnlock()
}
func (l *Logger) Debugf(format string, v ...interface{}) {
l.RLock()
l.Logger.Debug().Msgf(format, v...)
l.RUnlock()
}
func (l *Logger) Tracef(format string, v ...interface{}) {
l.RLock()
l.Logger.Trace().Msgf(format, v...)
l.RUnlock()
}
func (l *Logger) Error(v ...interface{}) {
l.RLock()
printLn(l.Logger.Error(), v...)
l.RUnlock()
}
func (l *Logger) Warn(v ...interface{}) {
l.RLock()
printLn(l.Logger.Warn(), v...)
l.RUnlock()
}
func (l *Logger) Info(v ...interface{}) {
l.RLock()
printLn(l.Logger.Info(), v...)
l.RUnlock()
}
func (l *Logger) Debug(v ...interface{}) {
l.RLock()
printLn(l.Logger.Debug(), v...)
l.RUnlock()
}
func (l *Logger) Trace(v ...interface{}) {
l.RLock()
printLn(l.Logger.Trace(), v...)
l.RUnlock()
}
func (l *Logger) Errorln(v ...interface{}) {
l.RLock()
printLn(l.Logger.Error(), v...)
l.RUnlock()
}
func (l *Logger) Warnln(v ...interface{}) {
l.RLock()
printLn(l.Logger.Warn(), v...)
l.RUnlock()
}
func (l *Logger) Infoln(v ...interface{}) {
l.RLock()
printLn(l.Logger.Info(), v...)
l.RUnlock()
}
func (l *Logger) Debugln(v ...interface{}) {
l.RLock()
printLn(l.Logger.Debug(), v...)
l.RUnlock()
}
func (l *Logger) Traceln(v ...interface{}) {
l.RLock()
printLn(l.Logger.Trace(), v...)
l.RUnlock()
}
func (l *Logger) Verbosef(format string, v ...interface{}) {
l.RLock()
l.Logger.Trace().Msgf(format, v...)
l.RUnlock()
}
func (l *Logger) Noticef(format string, v ...interface{}) {
l.RLock()
l.Logger.Info().Msgf(format, v...)
l.RUnlock()
}
func (l *Logger) Warningf(format string, v ...interface{}) {
l.RLock()
l.Logger.Warn().Msgf(format, v...)
l.RUnlock()
}
func (l *Logger) WithPrefix(prefix string) *Logger {
l.SetPrefix(prefix)
return l
}
func (l *Logger) Logf(format string, v ...interface{}) {
l.Printf(format, v...)
}
func (l *Logger) WithFields(fields map[string]interface{}) *Logger {
l.RLock()
nl := l.Logger.With().Fields(fields).Logger()
l.Logger = &nl
l.RUnlock()
return l
}
// SetLevel is compatibility for ghettovoice/gosip/log.Logger
func (l *Logger) SetLevel(level uint32) {
l.Lock()
nl := l.Logger.Level(gosipLevelToZerologLevel(level))
l.Logger = &nl
l.Unlock()
}
func gosipLevelToZerologLevel(level uint32) zerolog.Level {
switch level {
case 0:
return zerolog.PanicLevel
case 1:
return zerolog.FatalLevel
case 2:
return zerolog.ErrorLevel
case 3:
return zerolog.WarnLevel
case 4:
return zerolog.InfoLevel
case 5:
return zerolog.DebugLevel
case 6:
return zerolog.TraceLevel
}
panic(fmt.Sprintf("invalid log level %d", level))
}
func (l *Logger) Write(p []byte) (n int, err error) {
l.RLock()
l.Logger.WithLevel(l.printLevel).Msg(string(bytes.TrimSuffix(p, []byte("\n"))))
l.RUnlock()
return len(p), nil
}
func (l *Logger) Output(calldepth int, s string) error {
l.RLock()
event := l.Logger.Info()
if calldepth != 2 {
if l.prefix != "" {
zerolog.CallerFieldName = "caller_file"
}
event.CallerSkipFrame(calldepth)
event = event.Caller()
}
event.Msg(s)
zerolog.CallerFieldName = "caller"
l.RUnlock()
return nil
}
func printLn(e *zerolog.Event, v ...interface{}) {
strBuf := strBufs.Get().(*strings.Builder)
for i, val := range v {
if i > 0 {
strBuf.WriteString(" ")
}
strBuf.WriteString(fmt.Sprint(val))
}
e.Msg(strBuf.String())
strBuf.Reset()
strBufs.Put(strBuf)
}
type prefixHook struct {
parent StdCompatLogger
}
func (h prefixHook) Run(e *zerolog.Event, _ zerolog.Level, _ string) {
if h.parent.Prefix() != "" {
e.Str("caller", h.parent.Prefix())
}
}
func Wrap(l zerolog.Logger) *Logger {
wrapped := &Logger{
RWMutex: &sync.RWMutex{},
printLevel: zerolog.InfoLevel,
}
p := prefixHook{wrapped}
l = l.Hook(p)
wrapped.Logger = &l
return wrapped
}