Feat (breaking changes?): panic/fatal bypasses, force level, add unit tests
This commit is contained in:
parent
dd9ca58d52
commit
e99bc8f197
72
bypass.go
Normal file
72
bypass.go
Normal file
@ -0,0 +1,72 @@
|
||||
package zwrap
|
||||
|
||||
func (l *Logger) checkPanicBypass(fmt string, v ...interface{}) (string, []interface{}, bool) {
|
||||
if !l.noPanic {
|
||||
return fmt, v, true
|
||||
}
|
||||
|
||||
if fmt != "" {
|
||||
fmt = "[PANIC BYPASSED] " + fmt
|
||||
return fmt, v, false
|
||||
}
|
||||
|
||||
nv := make([]interface{}, len(v)+1)
|
||||
nv[0] = "[PANIC BYPASSED]"
|
||||
copy(nv[1:], v)
|
||||
v = nil
|
||||
return fmt, nv, false
|
||||
}
|
||||
|
||||
func (l *Logger) checkFatalBypass(fmt string, v ...interface{}) (string, []interface{}, bool) {
|
||||
if !l.noFatal {
|
||||
return fmt, v, true
|
||||
}
|
||||
|
||||
if fmt != "" {
|
||||
fmt = "[FATAL BYPASSED] " + fmt
|
||||
return fmt, v, false
|
||||
}
|
||||
|
||||
nv := make([]interface{}, len(v)+1)
|
||||
nv[0] = "[FATAL BYPASSED]"
|
||||
copy(nv[1:], v)
|
||||
v = nil
|
||||
return fmt, nv, false
|
||||
}
|
||||
|
||||
func (l *Logger) NoPanics(b bool) {
|
||||
l.mu.Lock()
|
||||
l.noPanic = b
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
func (l *Logger) NoFatals(b bool) {
|
||||
l.mu.Lock()
|
||||
l.noFatal = b
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
func (l *Logger) WithNoPanics() *Logger {
|
||||
l.NoPanics(true)
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *Logger) WithNoFatals() *Logger {
|
||||
l.NoFatals(true)
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *Logger) ForceLevel(level any) {
|
||||
l.mu.Lock()
|
||||
nl := castToZlogLevel(level)
|
||||
l.forceLevel = &nl
|
||||
l.printLevel = nl
|
||||
nll := l.Logger.Level(nl)
|
||||
l.Logger = &nll
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
func (l *Logger) WithForceLevel(level uint32) *Logger {
|
||||
l.ForceLevel(level)
|
||||
return l
|
||||
}
|
10
go.mod
10
go.mod
@ -1,11 +1,11 @@
|
||||
module git.tcp.direct/kayos/zwrap
|
||||
|
||||
go 1.18
|
||||
go 1.20
|
||||
|
||||
require github.com/rs/zerolog v1.30.0
|
||||
require github.com/rs/zerolog v1.32.0
|
||||
|
||||
require (
|
||||
github.com/mattn/go-colorable v0.1.12 // indirect
|
||||
github.com/mattn/go-isatty v0.0.14 // indirect
|
||||
golang.org/x/sys v0.18.0 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.19 // indirect
|
||||
golang.org/x/sys v0.20.0 // indirect
|
||||
)
|
||||
|
22
go.sum
22
go.sum
@ -1,14 +1,16 @@
|
||||
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
|
||||
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
|
||||
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
|
||||
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
|
||||
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
|
||||
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
|
||||
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/rs/xid v1.5.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
|
||||
github.com/rs/zerolog v1.30.0 h1:SymVODrcRsaRaSInD9yQtKbtWqwsfoPcRff/oRXLj4c=
|
||||
github.com/rs/zerolog v1.30.0/go.mod h1:/tk+P47gFdPXq4QYjvCmT5/Gsug2nagsFWBWhAiSi1w=
|
||||
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4=
|
||||
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
github.com/rs/zerolog v1.32.0 h1:keLypqrlIjaFsbmJOBdB/qvyF8KEtCWHwobLp5l/mQ0=
|
||||
github.com/rs/zerolog v1.32.0/go.mod h1:/7mN4D5sKwJLZQ2b/znpjC3/GQWY/xaDXUM0kKWRHss=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.20.0 h1:Od9JTbYCk261bKm4M/mw7AklTlFYIa0bIp9BgSm1S8Y=
|
||||
golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
|
84
level.go
Normal file
84
level.go
Normal file
@ -0,0 +1,84 @@
|
||||
package zwrap
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
type Level interface {
|
||||
int | uint | int8 | uint8 | int16 | uint16 | int32 | uint32 | int64 | uint64
|
||||
}
|
||||
|
||||
func castToZlogLevel(level any) zerolog.Level {
|
||||
switch casted := level.(type) {
|
||||
case int:
|
||||
return toZlogLevel[int](casted)
|
||||
case int8:
|
||||
return toZlogLevel[int8](casted)
|
||||
case int16:
|
||||
return toZlogLevel[int16](casted)
|
||||
case int32:
|
||||
return toZlogLevel[int32](casted)
|
||||
case int64:
|
||||
return toZlogLevel[int64](casted)
|
||||
case uint:
|
||||
return toZlogLevel[uint](casted)
|
||||
case uint8:
|
||||
return toZlogLevel[uint8](casted)
|
||||
case uint16:
|
||||
return toZlogLevel[uint16](casted)
|
||||
case uint32:
|
||||
return toZlogLevel[uint32](casted)
|
||||
case uint64:
|
||||
return toZlogLevel[uint64](casted)
|
||||
case string:
|
||||
if parsed, err := zerolog.ParseLevel(casted); err == nil {
|
||||
return parsed
|
||||
} else {
|
||||
panic(fmt.Sprintf("invalid log level string %v: %v", level, err))
|
||||
}
|
||||
case zerolog.Level:
|
||||
return casted
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid log level type (%T): %v", level, level))
|
||||
}
|
||||
}
|
||||
|
||||
func toZlogLevel[T Level](level T) zerolog.Level {
|
||||
switch casted := any(level).(type) {
|
||||
case uint32: // compat
|
||||
switch {
|
||||
case casted == 0:
|
||||
return zerolog.PanicLevel
|
||||
case casted == 1:
|
||||
return zerolog.FatalLevel
|
||||
case casted == 2:
|
||||
return zerolog.ErrorLevel
|
||||
case casted == 3:
|
||||
return zerolog.WarnLevel
|
||||
case casted == 4:
|
||||
return zerolog.InfoLevel
|
||||
case casted == 5:
|
||||
return zerolog.DebugLevel
|
||||
case casted == 6:
|
||||
return zerolog.TraceLevel
|
||||
default:
|
||||
if casted < 0 {
|
||||
return zerolog.TraceLevel
|
||||
}
|
||||
if casted > 5 {
|
||||
return zerolog.PanicLevel
|
||||
}
|
||||
}
|
||||
case int16, int32, int64, int, uint, uint8, uint16, uint64, int8:
|
||||
if level < 0 {
|
||||
return zerolog.TraceLevel
|
||||
}
|
||||
if level > 5 {
|
||||
return zerolog.PanicLevel
|
||||
}
|
||||
return zerolog.Level(int8(level))
|
||||
}
|
||||
return zerolog.TraceLevel
|
||||
}
|
108
level_test.go
Normal file
108
level_test.go
Normal file
@ -0,0 +1,108 @@
|
||||
package zwrap
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
func TestCastToZlogLevel(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
level any
|
||||
want zerolog.Level
|
||||
}{
|
||||
{"int", 3, zerolog.ErrorLevel},
|
||||
{"int8", int8(4), zerolog.FatalLevel},
|
||||
{"int16", int16(2), zerolog.WarnLevel},
|
||||
{"int32", int32(1), zerolog.InfoLevel},
|
||||
{"int64", int64(5), zerolog.PanicLevel},
|
||||
{"uint", uint(6), zerolog.PanicLevel},
|
||||
{"uint8", uint8(0), zerolog.DebugLevel},
|
||||
{"uint16", uint16(4), zerolog.FatalLevel},
|
||||
{"uint32", uint32(3), zerolog.WarnLevel},
|
||||
{"uint64", uint64(2), zerolog.WarnLevel},
|
||||
{"string", "info", zerolog.InfoLevel},
|
||||
{"zerolog.Level", zerolog.DebugLevel, zerolog.DebugLevel},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := castToZlogLevel(tt.level); got != tt.want {
|
||||
t.Errorf("castToZlogLevel() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestCastToZlogLevel_Panic(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
level any
|
||||
}{
|
||||
{"invalid type", 3.14},
|
||||
{"invalid string", "invalid"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r == nil {
|
||||
t.Errorf("castToZlogLevel() did not panic")
|
||||
}
|
||||
}()
|
||||
castToZlogLevel(tt.level)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestToZlogLevel(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
level any
|
||||
want zerolog.Level
|
||||
}{
|
||||
{"int8", int8(3), zerolog.ErrorLevel},
|
||||
{"int16", int16(2), zerolog.WarnLevel},
|
||||
{"int32", int32(1), zerolog.InfoLevel},
|
||||
{"int64", int64(5), zerolog.PanicLevel},
|
||||
{"uint", uint(0), zerolog.DebugLevel},
|
||||
{"uint8", uint8(6), zerolog.PanicLevel},
|
||||
{"uint16", uint16(4), zerolog.FatalLevel},
|
||||
{"uint32", uint32(3), zerolog.WarnLevel},
|
||||
{"uint64", uint64(2), zerolog.WarnLevel},
|
||||
{"-1 trace level", -1, zerolog.TraceLevel},
|
||||
{"> limit level", uint(7), zerolog.PanicLevel},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
switch casted := tt.level.(type) {
|
||||
case int8:
|
||||
if got := toZlogLevel[int8](casted); got != tt.want {
|
||||
t.Errorf("toZlogLevel(%v) = %v, want %v", tt.level, got, tt.want)
|
||||
}
|
||||
case int16:
|
||||
if got := toZlogLevel[int16](casted); got != tt.want {
|
||||
t.Errorf("toZlogLevel(%v) = %v, want %v", tt.level, got, tt.want)
|
||||
}
|
||||
case int32:
|
||||
if got := toZlogLevel[int32](casted); got != tt.want {
|
||||
t.Errorf("toZlogLevel(%v) = %v, want %v", tt.level, got, tt.want)
|
||||
}
|
||||
case int64:
|
||||
if got := toZlogLevel[int64](casted); got != tt.want {
|
||||
t.Errorf("toZlogLevel(%v) = %v, want %v", tt.level, got, tt.want)
|
||||
}
|
||||
case uint:
|
||||
if got := toZlogLevel[uint](casted); got != tt.want {
|
||||
t.Errorf("toZlogLevel(%v) = %v, want %v", tt.level, got, tt.want)
|
||||
}
|
||||
case uint8:
|
||||
if got := toZlogLevel[uint8](casted); got != tt.want {
|
||||
t.Errorf("toZlogLevel(%v) = %v, want %v", tt.level, got, tt.want)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
43
restore_colors_test.go
Normal file
43
restore_colors_test.go
Normal file
@ -0,0 +1,43 @@
|
||||
package zwrap
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/rs/zerolog"
|
||||
)
|
||||
|
||||
type colorTester struct {
|
||||
t *testing.T
|
||||
last []byte
|
||||
}
|
||||
|
||||
func (c *colorTester) Write(p []byte) (n int, err error) {
|
||||
if len(c.last) == 0 {
|
||||
c.last = make([]byte, len(p))
|
||||
copy(c.last, p)
|
||||
return len(p), nil
|
||||
}
|
||||
if bytes.Equal(c.last, p) {
|
||||
c.t.Errorf("\ncaught second output: %s\nwhich is the same as the first: %s", string(p), string(c.last))
|
||||
}
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func TestLegacyColorizer(t *testing.T) {
|
||||
tw := &colorTester{t: t}
|
||||
zlc := zerolog.NewConsoleWriter()
|
||||
zlc.Out = tw
|
||||
zlc.NoColor = false
|
||||
zl := Wrap(zerolog.New(zlc))
|
||||
zl.Trace("yeet")
|
||||
if len(tw.last) == 0 {
|
||||
t.Fatalf("test writer busted")
|
||||
}
|
||||
zlc = zerolog.NewConsoleWriter()
|
||||
zlc.FormatLevel = LogLevelFmt(false)
|
||||
zlc.NoColor = false
|
||||
zlc.Out = tw
|
||||
zl = Wrap(zerolog.New(zlc))
|
||||
zl.Trace("yeet")
|
||||
}
|
306
wrap.go
306
wrap.go
@ -13,18 +13,21 @@ import (
|
||||
|
||||
type Logger struct {
|
||||
*zerolog.Logger
|
||||
*sync.RWMutex
|
||||
mu *sync.RWMutex
|
||||
|
||||
prefix string
|
||||
printLevel zerolog.Level
|
||||
forceLevel *zerolog.Level
|
||||
noPanic bool
|
||||
noFatal bool
|
||||
}
|
||||
|
||||
func (l *Logger) Warning(args ...any) {
|
||||
l.Logger.Warn().Msg(fmt.Sprint(args...))
|
||||
l.printLn(l.Logger.Warn(), false, args...)
|
||||
}
|
||||
|
||||
func (l *Logger) Warningln(args ...any) {
|
||||
l.Logger.Warn().Msg(fmt.Sprintln(args...))
|
||||
l.printLn(l.Logger.Warn(), false, args...)
|
||||
}
|
||||
|
||||
func (l *Logger) V(level int) bool {
|
||||
@ -38,176 +41,226 @@ func (l *Logger) V(level int) bool {
|
||||
}
|
||||
|
||||
func (l *Logger) SetPrefix(prefix string) {
|
||||
l.Lock()
|
||||
l.mu.Lock()
|
||||
l.prefix = prefix
|
||||
l.Unlock()
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
func (l *Logger) SetPrintLevel(level zerolog.Level) {
|
||||
l.Lock()
|
||||
l.mu.Lock()
|
||||
l.printLevel = level
|
||||
l.Unlock()
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
func (l *Logger) Prefix() string {
|
||||
l.RLock()
|
||||
defer l.RUnlock()
|
||||
l.mu.RLock()
|
||||
p := l.myPrefix()
|
||||
l.mu.RUnlock()
|
||||
return p
|
||||
}
|
||||
|
||||
func (l *Logger) myPrefix() string {
|
||||
return l.prefix
|
||||
}
|
||||
|
||||
func (l *Logger) Println(v ...interface{}) {
|
||||
l.RLock()
|
||||
l.Logger.WithLevel(l.printLevel).Msg(fmt.Sprint(v...))
|
||||
l.RUnlock()
|
||||
l.mu.RLock()
|
||||
l.printLn(l.Logger.WithLevel(l.printLevel), false, v...)
|
||||
l.mu.RUnlock()
|
||||
}
|
||||
|
||||
func (l *Logger) Printf(format string, v ...interface{}) {
|
||||
l.RLock()
|
||||
l.Logger.WithLevel(l.printLevel).Msgf(format, v...)
|
||||
l.RUnlock()
|
||||
l.mu.RLock()
|
||||
var str string
|
||||
switch {
|
||||
case len(v) == 0:
|
||||
str = format
|
||||
case len(v) == 1:
|
||||
str = fmt.Sprintf(format, v[0])
|
||||
default:
|
||||
str = fmt.Sprintf(format, v...)
|
||||
}
|
||||
l.printLn(l.Logger.WithLevel(l.printLevel), false, str)
|
||||
l.mu.RUnlock()
|
||||
}
|
||||
|
||||
func (l *Logger) Print(v ...interface{}) {
|
||||
l.RLock()
|
||||
l.Logger.WithLevel(l.printLevel).Msg(fmt.Sprint(v...))
|
||||
l.RUnlock()
|
||||
l.mu.RLock()
|
||||
l.printLn(l.Logger.WithLevel(l.printLevel), false, v...)
|
||||
l.mu.RUnlock()
|
||||
}
|
||||
|
||||
func (l *Logger) Fatal(v ...interface{}) {
|
||||
// Don't check mutex here because we're exiting anyway.
|
||||
printLn(l.Logger.Fatal(), v...)
|
||||
var ok bool
|
||||
if _, v, ok = l.checkFatalBypass("", v...); ok {
|
||||
l.printLn(l.Logger.Fatal(), true, v...)
|
||||
return
|
||||
}
|
||||
l.mu.RLock()
|
||||
l.printLn(l.Logger.Error(), false, v...)
|
||||
l.mu.RUnlock()
|
||||
}
|
||||
|
||||
func (l *Logger) Fatalf(format string, v ...interface{}) {
|
||||
// Don't check mutex here because we're exiting anyway.
|
||||
l.Logger.Fatal().Msgf(format, v...)
|
||||
var ok bool
|
||||
if format, v, ok = l.checkFatalBypass(format, v...); ok {
|
||||
l.printLn(l.Logger.Fatal(), true, fmt.Sprintf(format, v...))
|
||||
return
|
||||
}
|
||||
l.mu.RLock()
|
||||
l.printLn(l.Logger.Error(), false, fmt.Sprintf(format, v...))
|
||||
l.mu.RUnlock()
|
||||
}
|
||||
|
||||
func (l *Logger) Fatalln(v ...interface{}) {
|
||||
// Don't check mutex here because we're exiting anyway.
|
||||
printLn(l.Logger.Fatal(), v...)
|
||||
var ok bool
|
||||
if _, v, ok = l.checkFatalBypass("", v...); ok {
|
||||
l.printLn(l.Logger.Fatal(), true, v...)
|
||||
return
|
||||
}
|
||||
l.mu.RLock()
|
||||
l.printLn(l.Logger.Error(), false, v...)
|
||||
l.mu.RUnlock()
|
||||
}
|
||||
|
||||
func (l *Logger) Panic(v ...interface{}) {
|
||||
// Don't check mutex here because we're panicking anyway.
|
||||
printLn(l.Logger.Panic(), v...)
|
||||
var ok bool
|
||||
if _, v, ok = l.checkPanicBypass("", v...); ok {
|
||||
l.printLn(l.Logger.Panic(), true, v...)
|
||||
return
|
||||
}
|
||||
l.mu.RLock()
|
||||
l.printLn(l.Logger.Error(), false, v...)
|
||||
l.mu.RUnlock()
|
||||
}
|
||||
|
||||
func (l *Logger) Panicf(format string, v ...interface{}) {
|
||||
// Don't check mutex here because we're panicking anyway.
|
||||
l.Logger.Panic().Msgf(format, v...)
|
||||
var ok bool
|
||||
if format, v, ok = l.checkPanicBypass(format, v...); ok {
|
||||
l.printLn(l.Logger.Panic(), true, fmt.Sprintf(format, v...))
|
||||
return
|
||||
}
|
||||
l.mu.RLock()
|
||||
l.printLn(l.Logger.Error(), false, fmt.Sprintf(format, v...))
|
||||
l.mu.RUnlock()
|
||||
}
|
||||
|
||||
func (l *Logger) Panicln(v ...interface{}) {
|
||||
// Don't check mutex here because we're panicking anyway.
|
||||
printLn(l.Logger.Panic(), v...)
|
||||
var ok bool
|
||||
if _, v, ok = l.checkPanicBypass("", v...); ok {
|
||||
l.printLn(l.Logger.Panic(), true, v...)
|
||||
return
|
||||
}
|
||||
l.mu.RLock()
|
||||
l.printLn(l.Logger.Error(), false, v...)
|
||||
l.mu.RUnlock()
|
||||
}
|
||||
|
||||
func (l *Logger) Errorf(format string, v ...interface{}) {
|
||||
l.RLock()
|
||||
l.Logger.Error().Msgf(format, v...)
|
||||
l.RUnlock()
|
||||
l.mu.RLock()
|
||||
l.printLn(l.Logger.Error(), false, fmt.Sprintf(format, v...))
|
||||
l.mu.RUnlock()
|
||||
}
|
||||
|
||||
func (l *Logger) Warnf(format string, v ...interface{}) {
|
||||
l.RLock()
|
||||
l.Logger.Warn().Msgf(format, v...)
|
||||
l.RUnlock()
|
||||
l.mu.RLock()
|
||||
l.printLn(l.Logger.Warn(), false, fmt.Sprintf(format, v...))
|
||||
l.mu.RUnlock()
|
||||
}
|
||||
|
||||
func (l *Logger) Infof(format string, v ...interface{}) {
|
||||
l.RLock()
|
||||
l.Logger.Info().Msgf(format, v...)
|
||||
l.RUnlock()
|
||||
l.mu.RLock()
|
||||
l.printLn(l.Logger.Info(), false, fmt.Sprintf(format, v...))
|
||||
l.mu.RUnlock()
|
||||
}
|
||||
|
||||
func (l *Logger) Debugf(format string, v ...interface{}) {
|
||||
l.RLock()
|
||||
l.Logger.Debug().Msgf(format, v...)
|
||||
l.RUnlock()
|
||||
l.mu.RLock()
|
||||
l.printLn(l.Logger.Debug(), false, fmt.Sprintf(format, v...))
|
||||
l.mu.RUnlock()
|
||||
}
|
||||
|
||||
func (l *Logger) Tracef(format string, v ...interface{}) {
|
||||
l.RLock()
|
||||
l.Logger.Trace().Msgf(format, v...)
|
||||
l.RUnlock()
|
||||
l.mu.RLock()
|
||||
l.printLn(l.Logger.Trace(), false, fmt.Sprintf(format, v...))
|
||||
l.mu.RUnlock()
|
||||
}
|
||||
|
||||
func (l *Logger) Error(v ...interface{}) {
|
||||
l.RLock()
|
||||
printLn(l.Logger.Error(), v...)
|
||||
l.RUnlock()
|
||||
l.mu.RLock()
|
||||
l.printLn(l.Logger.Error(), false, v...)
|
||||
l.mu.RUnlock()
|
||||
}
|
||||
|
||||
func (l *Logger) Warn(v ...interface{}) {
|
||||
l.RLock()
|
||||
printLn(l.Logger.Warn(), v...)
|
||||
l.RUnlock()
|
||||
l.mu.RLock()
|
||||
l.printLn(l.Logger.Warn(), false, v...)
|
||||
l.mu.RUnlock()
|
||||
}
|
||||
|
||||
func (l *Logger) Info(v ...interface{}) {
|
||||
l.RLock()
|
||||
printLn(l.Logger.Info(), v...)
|
||||
l.RUnlock()
|
||||
l.mu.RLock()
|
||||
l.printLn(l.Logger.Info(), false, v...)
|
||||
l.mu.RUnlock()
|
||||
}
|
||||
|
||||
func (l *Logger) Debug(v ...interface{}) {
|
||||
l.RLock()
|
||||
printLn(l.Logger.Debug(), v...)
|
||||
l.RUnlock()
|
||||
l.mu.RLock()
|
||||
l.printLn(l.Logger.Debug(), false, v...)
|
||||
l.mu.RUnlock()
|
||||
}
|
||||
|
||||
func (l *Logger) Trace(v ...interface{}) {
|
||||
l.RLock()
|
||||
printLn(l.Logger.Trace(), v...)
|
||||
l.RUnlock()
|
||||
l.mu.RLock()
|
||||
l.printLn(l.Logger.Trace(), false, v...)
|
||||
l.mu.RUnlock()
|
||||
}
|
||||
|
||||
func (l *Logger) Errorln(v ...interface{}) {
|
||||
l.RLock()
|
||||
printLn(l.Logger.Error(), v...)
|
||||
l.RUnlock()
|
||||
l.mu.RLock()
|
||||
l.printLn(l.Logger.Error(), false, v...)
|
||||
l.mu.RUnlock()
|
||||
}
|
||||
|
||||
func (l *Logger) Warnln(v ...interface{}) {
|
||||
l.RLock()
|
||||
printLn(l.Logger.Warn(), v...)
|
||||
l.RUnlock()
|
||||
l.mu.RLock()
|
||||
l.printLn(l.Logger.Warn(), false, v...)
|
||||
l.mu.RUnlock()
|
||||
}
|
||||
|
||||
func (l *Logger) Infoln(v ...interface{}) {
|
||||
l.RLock()
|
||||
printLn(l.Logger.Info(), v...)
|
||||
l.RUnlock()
|
||||
l.mu.RLock()
|
||||
l.printLn(l.Logger.Info(), false, v...)
|
||||
l.mu.RUnlock()
|
||||
}
|
||||
|
||||
func (l *Logger) Debugln(v ...interface{}) {
|
||||
l.RLock()
|
||||
printLn(l.Logger.Debug(), v...)
|
||||
l.RUnlock()
|
||||
l.mu.RLock()
|
||||
l.printLn(l.Logger.Debug(), false, v...)
|
||||
l.mu.RUnlock()
|
||||
}
|
||||
|
||||
func (l *Logger) Traceln(v ...interface{}) {
|
||||
l.RLock()
|
||||
printLn(l.Logger.Trace(), v...)
|
||||
l.RUnlock()
|
||||
l.mu.RLock()
|
||||
l.printLn(l.Logger.Trace(), false, v...)
|
||||
l.mu.RUnlock()
|
||||
}
|
||||
|
||||
func (l *Logger) Verbosef(format string, v ...interface{}) {
|
||||
l.RLock()
|
||||
l.Logger.Trace().Msgf(format, v...)
|
||||
l.RUnlock()
|
||||
l.mu.RLock()
|
||||
l.printLn(l.Logger.Trace(), false, fmt.Sprintf(format, v...))
|
||||
l.mu.RUnlock()
|
||||
}
|
||||
|
||||
func (l *Logger) Noticef(format string, v ...interface{}) {
|
||||
l.RLock()
|
||||
l.Logger.Info().Msgf(format, v...)
|
||||
l.RUnlock()
|
||||
l.mu.RLock()
|
||||
l.printLn(l.Logger.Info(), false, fmt.Sprintf(format, v...))
|
||||
l.mu.RUnlock()
|
||||
}
|
||||
func (l *Logger) Warningf(format string, v ...interface{}) {
|
||||
l.RLock()
|
||||
l.Logger.Warn().Msgf(format, v...)
|
||||
l.RUnlock()
|
||||
l.mu.RLock()
|
||||
l.printLn(l.Logger.Warn(), false, fmt.Sprintf(format, v...))
|
||||
l.mu.RUnlock()
|
||||
}
|
||||
|
||||
func (l *Logger) WithPrefix(prefix string) *Logger {
|
||||
@ -220,50 +273,30 @@ func (l *Logger) Logf(format string, v ...interface{}) {
|
||||
}
|
||||
|
||||
func (l *Logger) WithFields(fields map[string]interface{}) *Logger {
|
||||
l.RLock()
|
||||
l.mu.RLock()
|
||||
nl := l.Logger.With().Fields(fields).Logger()
|
||||
l.Logger = &nl
|
||||
l.RUnlock()
|
||||
l.mu.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))
|
||||
func (l *Logger) SetLevel(level any) {
|
||||
l.mu.Lock()
|
||||
nl := l.Logger.Level(castToZlogLevel(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))
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
func (l *Logger) Write(p []byte) (n int, err error) {
|
||||
l.RLock()
|
||||
l.mu.RLock()
|
||||
l.Logger.WithLevel(l.printLevel).Msg(string(bytes.TrimSuffix(p, []byte("\n"))))
|
||||
l.RUnlock()
|
||||
l.mu.RUnlock()
|
||||
return len(p), nil
|
||||
}
|
||||
|
||||
func (l *Logger) Output(calldepth int, s string) error {
|
||||
l.RLock()
|
||||
l.mu.RLock()
|
||||
event := l.Logger.Info()
|
||||
if calldepth != 2 {
|
||||
if l.prefix != "" {
|
||||
@ -274,11 +307,48 @@ func (l *Logger) Output(calldepth int, s string) error {
|
||||
}
|
||||
event.Msg(s)
|
||||
zerolog.CallerFieldName = "caller"
|
||||
l.RUnlock()
|
||||
l.mu.RUnlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func printLn(e *zerolog.Event, v ...interface{}) {
|
||||
func (l *Logger) transformZEvent(e *zerolog.Event) *zerolog.Event {
|
||||
switch *l.forceLevel {
|
||||
case zerolog.PanicLevel:
|
||||
if !l.noPanic {
|
||||
e = l.Logger.Panic()
|
||||
}
|
||||
case zerolog.FatalLevel:
|
||||
if !l.noFatal {
|
||||
e = l.Logger.Fatal()
|
||||
}
|
||||
case zerolog.ErrorLevel:
|
||||
e = l.Logger.Error()
|
||||
case zerolog.WarnLevel:
|
||||
e = l.Logger.Warn()
|
||||
case zerolog.InfoLevel:
|
||||
e = l.Logger.Info()
|
||||
case zerolog.DebugLevel:
|
||||
e = l.Logger.Debug()
|
||||
case zerolog.TraceLevel:
|
||||
e = l.Logger.Trace()
|
||||
default:
|
||||
panic(fmt.Sprintf("invalid logger config, bad force level %v", l.forceLevel))
|
||||
}
|
||||
return e
|
||||
}
|
||||
|
||||
func (l *Logger) printLn(e *zerolog.Event, preserve bool, v ...interface{}) {
|
||||
if l.forceLevel != nil && !preserve {
|
||||
e = l.transformZEvent(e)
|
||||
}
|
||||
if len(v) == 0 {
|
||||
e.Msg("")
|
||||
return
|
||||
}
|
||||
if len(v) == 1 {
|
||||
e.Msg(fmt.Sprint(v[0]))
|
||||
return
|
||||
}
|
||||
strBuf := strBufs.Get().(*strings.Builder)
|
||||
for i, val := range v {
|
||||
if i > 0 {
|
||||
@ -292,18 +362,18 @@ func printLn(e *zerolog.Event, v ...interface{}) {
|
||||
}
|
||||
|
||||
type prefixHook struct {
|
||||
parent StdCompatLogger
|
||||
parent *Logger
|
||||
}
|
||||
|
||||
func (h prefixHook) Run(e *zerolog.Event, _ zerolog.Level, _ string) {
|
||||
if h.parent.Prefix() != "" {
|
||||
e.Str("caller", h.parent.Prefix())
|
||||
e.Str("caller", h.parent.myPrefix())
|
||||
}
|
||||
}
|
||||
|
||||
func Wrap(l zerolog.Logger) *Logger {
|
||||
wrapped := &Logger{
|
||||
RWMutex: &sync.RWMutex{},
|
||||
mu: &sync.RWMutex{},
|
||||
printLevel: zerolog.InfoLevel,
|
||||
}
|
||||
p := prefixHook{wrapped}
|
||||
|
243
wrap_test.go
243
wrap_test.go
@ -14,6 +14,7 @@ type testWriter struct {
|
||||
t *testing.T
|
||||
needsPrefix string
|
||||
mustNotHavePrefix string
|
||||
mustLevel *zerolog.Level
|
||||
}
|
||||
|
||||
var ErrPrefixMismatch = errors.New("prefix mismatch")
|
||||
@ -29,6 +30,20 @@ func (w *testWriter) Write(p []byte) (n int, err error) {
|
||||
w.t.Errorf("unexpected prefix %q, got %q", w.mustNotHavePrefix, line)
|
||||
return 0, ErrPrefixMismatch
|
||||
}
|
||||
|
||||
if w.mustLevel != nil {
|
||||
if !strings.Contains(line, w.mustLevel.String()) {
|
||||
w.t.Errorf("expected level %q, got %q", w.mustLevel.String(), line)
|
||||
return 0, ErrPrefixMismatch
|
||||
}
|
||||
lvl := strings.Split(line, `"level":"`)[1]
|
||||
lvl = strings.Split(lvl, `"`)[0]
|
||||
if lvl != w.mustLevel.String() {
|
||||
w.t.Errorf("expected level %q, got %q", w.mustLevel.String(), lvl)
|
||||
return 0, ErrPrefixMismatch
|
||||
}
|
||||
}
|
||||
|
||||
w.t.Log(line)
|
||||
return len(p), nil
|
||||
}
|
||||
@ -46,7 +61,44 @@ func (n *needsLogger) SetLogger(logger aLogger) {
|
||||
}
|
||||
|
||||
func (n *needsLogger) DoSomething() {
|
||||
n.logger.Println("Hello, world!")
|
||||
n.logger.Println("yeet")
|
||||
}
|
||||
|
||||
type leveled struct {
|
||||
name string
|
||||
test func(*Logger, *testing.T)
|
||||
shouldPanic bool
|
||||
panicked bool
|
||||
t *testing.T
|
||||
}
|
||||
|
||||
const (
|
||||
expected = "expected"
|
||||
unexpected = "unexpected"
|
||||
)
|
||||
|
||||
func (l *leveled) expS() string {
|
||||
if l.shouldPanic {
|
||||
return expected
|
||||
}
|
||||
return unexpected
|
||||
}
|
||||
|
||||
func (l *leveled) Run() {
|
||||
l.t.Run(l.name, func(t *testing.T) {
|
||||
defer func() {
|
||||
if r := recover(); r != nil {
|
||||
l.t.Logf("caught panic (%s): %v", l.expS(), r)
|
||||
l.panicked = true
|
||||
}
|
||||
}()
|
||||
zl := zerolog.New(os.Stderr).With().Timestamp().Logger()
|
||||
wrapped := Wrap(zl)
|
||||
l.test(wrapped, t)
|
||||
})
|
||||
if (l.shouldPanic && !l.panicked) || (!l.shouldPanic && l.panicked) {
|
||||
l.t.Errorf("%s panic during test: %s", l.expS(), l.name)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWrap(t *testing.T) {
|
||||
@ -55,56 +107,209 @@ func TestWrap(t *testing.T) {
|
||||
writah := &testWriter{t: t}
|
||||
|
||||
zl := zerolog.New(writah).With().Timestamp().Logger()
|
||||
wrapped := Wrap(zl)
|
||||
wzl := Wrap(zl)
|
||||
myThing := &needsLogger{}
|
||||
myThing.SetLogger(wrapped)
|
||||
myThing.SetLogger(wzl)
|
||||
|
||||
multiLog := func(v ...interface{}) {
|
||||
multiLog := func(wrapped *Logger, v ...interface{}) {
|
||||
t.Helper()
|
||||
toggleFatals := wrapped.noFatal == false
|
||||
togglePanics := wrapped.noPanic == false
|
||||
wrapped.Print(v...)
|
||||
wrapped.Printf("%v", v)
|
||||
wrapped.Printf("f: %v", v...)
|
||||
wrapped.Println(v...)
|
||||
wrapped.Error(v...)
|
||||
wrapped.Errorf("%v", v)
|
||||
wrapped.Errorf("f: %v", v...)
|
||||
wrapped.Errorln(v...)
|
||||
wrapped.Debug(v...)
|
||||
wrapped.Debugf("%v", v)
|
||||
wrapped.Debugf("f: %v", v...)
|
||||
wrapped.Debugln(v...)
|
||||
wrapped.Warn(v...)
|
||||
wrapped.Warnf("%v", v)
|
||||
wrapped.Warnf("f: %v", v...)
|
||||
wrapped.Warnln(v...)
|
||||
wrapped.Info(v...)
|
||||
wrapped.Infof("%v", v)
|
||||
wrapped.Infof("f: %v", v...)
|
||||
wrapped.Infoln(v...)
|
||||
wrapped.Tracef("%v", v)
|
||||
wrapped.Tracef("f: %v", v...)
|
||||
wrapped.Trace(v...)
|
||||
wrapped.Traceln(v...)
|
||||
wrapped.Logf("%v", v)
|
||||
wrapped.Warning("%v", v)
|
||||
wrapped.Warningf("%v", v)
|
||||
wrapped.Logf("f: %v", v...)
|
||||
wrapped.Warning(v...)
|
||||
wrapped.Warningf("f: %v", v...)
|
||||
wrapped.Println("")
|
||||
wrapped.Println()
|
||||
if toggleFatals {
|
||||
wrapped.NoFatals(true)
|
||||
}
|
||||
wrapped.Fatal(v...)
|
||||
wrapped.Fatalf("f: %v", v...)
|
||||
wrapped.Fatalln(v...)
|
||||
if toggleFatals {
|
||||
wrapped.NoFatals(false)
|
||||
}
|
||||
if togglePanics {
|
||||
wrapped.NoPanics(true)
|
||||
}
|
||||
wrapped.Panic(v...)
|
||||
wrapped.Panicf("f: %v", v...)
|
||||
wrapped.Panicln(v...)
|
||||
if togglePanics {
|
||||
wrapped.NoPanics(false)
|
||||
}
|
||||
}
|
||||
|
||||
if wrapped.V(0) {
|
||||
if wzl.V(0) {
|
||||
t.Error("V(0) should always return false")
|
||||
}
|
||||
|
||||
t.Run("generic", func(t *testing.T) {
|
||||
multiLog("Hello, world!")
|
||||
multiLog(wzl, "yeet")
|
||||
})
|
||||
|
||||
t.Run("prefix", func(t *testing.T) {
|
||||
writah.needsPrefix = "prefix: "
|
||||
wrapped.SetPrefix("prefix: ")
|
||||
multiLog("Hello, world!")
|
||||
wzl.SetPrefix("prefix: ")
|
||||
multiLog(wzl, "yeet")
|
||||
writah.needsPrefix = "prefix2: "
|
||||
wzl = wzl.WithPrefix("prefix2: ")
|
||||
multiLog(wzl, "yeet")
|
||||
})
|
||||
|
||||
t.Run("remove prefix", func(t *testing.T) {
|
||||
writah.needsPrefix = ""
|
||||
writah.mustNotHavePrefix = "prefix: "
|
||||
wrapped.SetPrefix("")
|
||||
multiLog("Hello, world!")
|
||||
wzl.SetPrefix("")
|
||||
multiLog(wzl, "yeet")
|
||||
})
|
||||
|
||||
forceLevelTests := []leveled{
|
||||
{
|
||||
name: "trace",
|
||||
test: func(wrapped *Logger, t *testing.T) {
|
||||
wrapped.ForceLevel(zerolog.TraceLevel)
|
||||
multiLog(wrapped, "yeet")
|
||||
},
|
||||
shouldPanic: false,
|
||||
t: t,
|
||||
},
|
||||
{
|
||||
name: "trace_with_panic",
|
||||
test: func(wrapped *Logger, t *testing.T) {
|
||||
wrapped.ForceLevel(zerolog.TraceLevel)
|
||||
multiLog(wrapped, "yeet")
|
||||
wrapped.Panic("yeet")
|
||||
},
|
||||
shouldPanic: true,
|
||||
t: t,
|
||||
},
|
||||
{
|
||||
name: "debug",
|
||||
test: func(wrapped *Logger, t *testing.T) {
|
||||
wrapped.ForceLevel(zerolog.DebugLevel)
|
||||
multiLog(wrapped, "yeet")
|
||||
},
|
||||
shouldPanic: false,
|
||||
t: t,
|
||||
},
|
||||
{
|
||||
name: "info",
|
||||
test: func(wrapped *Logger, t *testing.T) {
|
||||
wrapped.ForceLevel(zerolog.InfoLevel)
|
||||
multiLog(wrapped, "yeet")
|
||||
},
|
||||
shouldPanic: false,
|
||||
t: t,
|
||||
},
|
||||
{
|
||||
name: "warn",
|
||||
test: func(wrapped *Logger, t *testing.T) {
|
||||
wrapped.ForceLevel(zerolog.WarnLevel)
|
||||
multiLog(wrapped, "yeet")
|
||||
},
|
||||
shouldPanic: false,
|
||||
t: t,
|
||||
},
|
||||
{
|
||||
name: "error",
|
||||
test: func(wrapped *Logger, t *testing.T) {
|
||||
wrapped.ForceLevel(zerolog.ErrorLevel)
|
||||
multiLog(wrapped, "yeet")
|
||||
},
|
||||
shouldPanic: false,
|
||||
t: t,
|
||||
},
|
||||
{
|
||||
name: "fatal",
|
||||
test: func(wrapped *Logger, t *testing.T) {
|
||||
wrapped.ForceLevel(zerolog.FatalLevel)
|
||||
wrapped.NoFatals(true)
|
||||
multiLog(wrapped, "yeet")
|
||||
},
|
||||
shouldPanic: false,
|
||||
t: t,
|
||||
},
|
||||
{
|
||||
name: "panic",
|
||||
test: func(wrapped *Logger, t *testing.T) {
|
||||
wrapped.ForceLevel(zerolog.PanicLevel)
|
||||
multiLog(wrapped, "yeet")
|
||||
},
|
||||
shouldPanic: true,
|
||||
t: t,
|
||||
},
|
||||
{
|
||||
name: "panic_with_panic",
|
||||
test: func(wrapped *Logger, t *testing.T) {
|
||||
wrapped.Panic("yeet")
|
||||
},
|
||||
shouldPanic: true,
|
||||
t: t,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range forceLevelTests {
|
||||
test.name = "force_level_" + test.name
|
||||
test.Run()
|
||||
}
|
||||
|
||||
panicAndFatalBypassTests := []leveled{
|
||||
{
|
||||
name: "no_panic",
|
||||
test: func(wrapped *Logger, t *testing.T) {
|
||||
wrapped.NoPanics(true)
|
||||
wrapped.Panic("yeet!!")
|
||||
},
|
||||
shouldPanic: false,
|
||||
t: t,
|
||||
},
|
||||
{
|
||||
name: "no_fatal",
|
||||
test: func(wrapped *Logger, t *testing.T) {
|
||||
wrapped.NoFatals(true)
|
||||
wrapped.Fatal("yeet")
|
||||
},
|
||||
shouldPanic: false, // I guess the test should fail if it os.Exits anyway..? :^)
|
||||
t: t,
|
||||
},
|
||||
{
|
||||
name: "no_panic_no_fatal_force_panic",
|
||||
test: func(wrapped *Logger, t *testing.T) {
|
||||
wrapped.NoPanics(true)
|
||||
wrapped.NoFatals(true)
|
||||
wrapped.ForceLevel(zerolog.PanicLevel)
|
||||
multiLog(wrapped, "yeet!!")
|
||||
wrapped.Panic("yeet!!")
|
||||
},
|
||||
shouldPanic: false,
|
||||
t: t,
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range panicAndFatalBypassTests {
|
||||
test.name = "bypass_" + test.name
|
||||
test.Run()
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func ExampleWrap() {
|
||||
|
Loading…
Reference in New Issue
Block a user