This change
- Removes interface `log.Format`,
- Removes method `log.FormatFunc`,
- unexports `TerminalHandler.TerminalFormat` formatting methods (renamed to `TerminalHandler.format`)
- removes the notion of `log.Lazy` values
The lazy handler was useful in the old log package, since it
could defer the evaluation of costly attributes until later in the
log pipeline: thus, if the logging was done at 'Trace', we could
skip evaluation if logging only was set to 'Info'.
With the move to slog, this way of deferring evaluation is no longer
needed, since slog introduced 'Enabled': the caller can thus do
the evaluate-or-not decision at the callsite, which is much more
straight-forward than dealing with lazy reflect-based evaluation.
Also, lazy evaluation would not work with 'native' slog, as in, these
two statements would be evaluated differently:
```golang
log.Info("foo", "my lazy", lazyObj)
slog.Info("foo", "my lazy", lazyObj)
```
173 lines
4.6 KiB
Go
173 lines
4.6 KiB
Go
package log
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"math/big"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/holiman/uint256"
|
|
"golang.org/x/exp/slog"
|
|
)
|
|
|
|
// TestLoggingWithVmodule checks that vmodule works.
|
|
func TestLoggingWithVmodule(t *testing.T) {
|
|
out := new(bytes.Buffer)
|
|
glog := NewGlogHandler(NewTerminalHandlerWithLevel(out, LevelTrace, false))
|
|
glog.Verbosity(LevelCrit)
|
|
logger := NewLogger(glog)
|
|
logger.Warn("This should not be seen", "ignored", "true")
|
|
glog.Vmodule("logger_test.go=5")
|
|
logger.Trace("a message", "foo", "bar")
|
|
have := out.String()
|
|
// The timestamp is locale-dependent, so we want to trim that off
|
|
// "INFO [01-01|00:00:00.000] a messag ..." -> "a messag..."
|
|
have = strings.Split(have, "]")[1]
|
|
want := " a message foo=bar\n"
|
|
if have != want {
|
|
t.Errorf("\nhave: %q\nwant: %q\n", have, want)
|
|
}
|
|
}
|
|
|
|
func TestTerminalHandlerWithAttrs(t *testing.T) {
|
|
out := new(bytes.Buffer)
|
|
glog := NewGlogHandler(NewTerminalHandlerWithLevel(out, LevelTrace, false).WithAttrs([]slog.Attr{slog.String("baz", "bat")}))
|
|
glog.Verbosity(LevelTrace)
|
|
logger := NewLogger(glog)
|
|
logger.Trace("a message", "foo", "bar")
|
|
have := out.String()
|
|
// The timestamp is locale-dependent, so we want to trim that off
|
|
// "INFO [01-01|00:00:00.000] a messag ..." -> "a messag..."
|
|
have = strings.Split(have, "]")[1]
|
|
want := " a message baz=bat foo=bar\n"
|
|
if have != want {
|
|
t.Errorf("\nhave: %q\nwant: %q\n", have, want)
|
|
}
|
|
}
|
|
|
|
func BenchmarkTraceLogging(b *testing.B) {
|
|
SetDefault(NewLogger(NewTerminalHandler(os.Stderr, true)))
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
Trace("a message", "v", i)
|
|
}
|
|
}
|
|
|
|
func BenchmarkTerminalHandler(b *testing.B) {
|
|
l := NewLogger(NewTerminalHandler(io.Discard, false))
|
|
benchmarkLogger(b, l)
|
|
}
|
|
func BenchmarkLogfmtHandler(b *testing.B) {
|
|
l := NewLogger(LogfmtHandler(io.Discard))
|
|
benchmarkLogger(b, l)
|
|
}
|
|
|
|
func BenchmarkJSONHandler(b *testing.B) {
|
|
l := NewLogger(JSONHandler(io.Discard))
|
|
benchmarkLogger(b, l)
|
|
}
|
|
|
|
func benchmarkLogger(b *testing.B, l Logger) {
|
|
var (
|
|
bb = make([]byte, 10)
|
|
tt = time.Now()
|
|
bigint = big.NewInt(100)
|
|
nilbig *big.Int
|
|
err = fmt.Errorf("Oh nooes it's crap")
|
|
)
|
|
b.ReportAllocs()
|
|
b.ResetTimer()
|
|
for i := 0; i < b.N; i++ {
|
|
l.Info("This is a message",
|
|
"foo", int16(i),
|
|
"bytes", bb,
|
|
"bonk", "a string with text",
|
|
"time", tt,
|
|
"bigint", bigint,
|
|
"nilbig", nilbig,
|
|
"err", err)
|
|
}
|
|
b.StopTimer()
|
|
}
|
|
|
|
func TestLoggerOutput(t *testing.T) {
|
|
type custom struct {
|
|
A string
|
|
B int8
|
|
}
|
|
var (
|
|
customA = custom{"Foo", 12}
|
|
customB = custom{"Foo\nLinebreak", 122}
|
|
bb = make([]byte, 10)
|
|
tt = time.Time{}
|
|
bigint = big.NewInt(100)
|
|
nilbig *big.Int
|
|
err = fmt.Errorf("Oh nooes it's crap")
|
|
smallUint = uint256.NewInt(500_000)
|
|
bigUint = &uint256.Int{0xff, 0xff, 0xff, 0xff}
|
|
)
|
|
|
|
out := new(bytes.Buffer)
|
|
glogHandler := NewGlogHandler(NewTerminalHandler(out, false))
|
|
glogHandler.Verbosity(LevelInfo)
|
|
NewLogger(glogHandler).Info("This is a message",
|
|
"foo", int16(123),
|
|
"bytes", bb,
|
|
"bonk", "a string with text",
|
|
"time", tt,
|
|
"bigint", bigint,
|
|
"nilbig", nilbig,
|
|
"err", err,
|
|
"struct", customA,
|
|
"struct", customB,
|
|
"ptrstruct", &customA,
|
|
"smalluint", smallUint,
|
|
"bigUint", bigUint)
|
|
|
|
have := out.String()
|
|
t.Logf("output %v", out.String())
|
|
want := `INFO [11-07|19:14:33.821] This is a message foo=123 bytes="[0 0 0 0 0 0 0 0 0 0]" bonk="a string with text" time=0001-01-01T00:00:00+0000 bigint=100 nilbig=<nil> err="Oh nooes it's crap" struct="{A:Foo B:12}" struct="{A:Foo\nLinebreak B:122}" ptrstruct="&{A:Foo B:12}" smalluint=500,000 bigUint=1,600,660,942,523,603,594,864,898,306,482,794,244,293,965,082,972,225,630,372,095
|
|
`
|
|
if !bytes.Equal([]byte(have)[25:], []byte(want)[25:]) {
|
|
t.Errorf("Error\nhave: %q\nwant: %q", have, want)
|
|
}
|
|
}
|
|
|
|
const termTimeFormat = "01-02|15:04:05.000"
|
|
|
|
func BenchmarkAppendFormat(b *testing.B) {
|
|
var now = time.Now()
|
|
b.Run("fmt time.Format", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
fmt.Fprintf(io.Discard, "%s", now.Format(termTimeFormat))
|
|
}
|
|
})
|
|
b.Run("time.AppendFormat", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
now.AppendFormat(nil, termTimeFormat)
|
|
}
|
|
})
|
|
var buf = new(bytes.Buffer)
|
|
b.Run("time.Custom", func(b *testing.B) {
|
|
for i := 0; i < b.N; i++ {
|
|
writeTimeTermFormat(buf, now)
|
|
buf.Reset()
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestTermTimeFormat(t *testing.T) {
|
|
var now = time.Now()
|
|
want := now.AppendFormat(nil, termTimeFormat)
|
|
var b = new(bytes.Buffer)
|
|
writeTimeTermFormat(b, now)
|
|
have := b.Bytes()
|
|
if !bytes.Equal(have, want) {
|
|
t.Errorf("have != want\nhave: %q\nwant: %q\n", have, want)
|
|
}
|
|
}
|