cosmos-sdk/log/bench_test.go

161 lines
3.8 KiB
Go

package log_test
import (
"bytes"
"errors"
"io"
"testing"
"time"
"cosmossdk.io/log"
"github.com/rs/zerolog"
)
const message = "test message"
func BenchmarkLoggers(b *testing.B) {
b.ReportAllocs()
type benchCase struct {
name string
keyVals []any
}
// Just test two simple cases for the nop logger benchmarks.
nopCases := []benchCase{
{name: "empty key vals"},
{name: "single string", keyVals: []any{"foo", "bar"}},
}
benchCases := append(nopCases, []benchCase{
{
name: "single small int",
keyVals: []any{"foo", 1},
},
{
// Small numbers may be optimized, so check if an unusual/larger number performs different.
name: "single largeish int",
keyVals: []any{"foo", 123456789},
},
{
name: "single float",
keyVals: []any{"foo", 2.71828182},
},
{
name: "single byte slice",
keyVals: []any{"foo", []byte{0xde, 0xad, 0xbe, 0xef}},
},
{
name: "single duration",
keyVals: []any{"foo", 10 * time.Second},
},
{
name: "two values",
keyVals: []any{"foo", "foo", "bar", "bar"},
},
{
name: "four values",
keyVals: []any{"foo", "foo", "bar", "bar", "baz", "baz", "quux", "quux"},
},
{
name: "eight values",
keyVals: []any{"one", 1, "two", 2, "three", 3, "four", 4, "five", 5, "six", 6, "seven", 7, "eight", 8},
},
}...)
// If running with "go test -v", print out the log messages as a sanity check.
if testing.Verbose() {
checkBuf := new(bytes.Buffer)
for _, bc := range benchCases {
checkBuf.Reset()
zl := zerolog.New(checkBuf)
logger := log.NewCustomLogger(zl)
logger.Info(message, bc.keyVals...)
b.Logf("zero logger output for %s: %s", bc.name, checkBuf.String())
}
}
// The real logger exposed by this package,
// writing to an io.Discard writer,
// so that real write time is negligible.
b.Run("zerolog", func(b *testing.B) {
for _, bc := range benchCases {
bc := bc
b.Run(bc.name, func(b *testing.B) {
zl := zerolog.New(io.Discard)
logger := log.NewCustomLogger(zl)
for i := 0; i < b.N; i++ {
logger.Info(message, bc.keyVals...)
}
})
}
})
// The nop logger we use expose in the public API,
// also useful as a reference for how expensive zerolog is.
b.Run("specialized nop logger", func(b *testing.B) {
for _, bc := range nopCases {
bc := bc
b.Run(bc.name, func(b *testing.B) {
logger := log.NewNopLogger()
for i := 0; i < b.N; i++ {
logger.Info(message, bc.keyVals...)
}
})
}
})
// To compare with the custom nop logger.
// The zerolog wrapper is about 1/3 the speed of the specialized nop logger,
// so we offer the specialized version in the exported API.
b.Run("zerolog nop logger", func(b *testing.B) {
for _, bc := range nopCases {
bc := bc
b.Run(bc.name, func(b *testing.B) {
logger := log.NewCustomLogger(zerolog.Nop())
for i := 0; i < b.N; i++ {
logger.Info(message, bc.keyVals...)
}
})
}
})
}
func BenchmarkLoggers_StructuredVsFields(b *testing.B) {
b.ReportAllocs()
errorToLog := errors.New("error")
byteSliceToLog := []byte{0xde, 0xad, 0xbe, 0xef}
b.Run("logger structured", func(b *testing.B) {
zl := zerolog.New(io.Discard)
var logger log.Logger = log.NewCustomLogger(zl)
zerolog := logger.Impl().(*zerolog.Logger)
for i := 0; i < b.N; i++ {
zerolog.Info().Int64("foo", 100000).Msg(message)
zerolog.Info().Str("foo", "foo").Msg(message)
zerolog.Error().
Int64("foo", 100000).
Str("bar", "foo").
Bytes("other", byteSliceToLog).
Err(errorToLog).
Msg(message)
}
})
b.Run("logger", func(b *testing.B) {
zl := zerolog.New(io.Discard)
var logger log.Logger = log.NewCustomLogger(zl)
for i := 0; i < b.N; i++ {
logger.Info(message, "foo", 100000)
logger.Info(message, "foo", "foo")
logger.Error(message, "foo", 100000, "bar", "foo", "other", byteSliceToLog, "error", errorToLog)
}
})
}