ffbd37b8d7
This adds the Func log method to log using an anonymous function only if the level is currently enabled. The use case is for when you don't own an object and therefore can't create your own marshaller but need to do some translation prior to logging. For example, this: msg := log.Debug() if msg.Enabled() { msg.Str("complicated_thing", makeBinaryThingLoggable(thing)) } msg.Msg("Sending complicated thing") Turns into this: log.Debug(). Func(func(e *Event) { e.Str("complicated_thing", makeBinaryThingLoggable(thing)) }). Msg("Sending complicated thing")
870 lines
27 KiB
Go
870 lines
27 KiB
Go
package zerolog
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"net"
|
|
"reflect"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestLog(t *testing.T) {
|
|
t.Run("empty", func(t *testing.T) {
|
|
out := &bytes.Buffer{}
|
|
log := New(out)
|
|
log.Log().Msg("")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), "{}\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
})
|
|
|
|
t.Run("one-field", func(t *testing.T) {
|
|
out := &bytes.Buffer{}
|
|
log := New(out)
|
|
log.Log().Str("foo", "bar").Msg("")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"foo":"bar"}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
})
|
|
|
|
t.Run("two-field", func(t *testing.T) {
|
|
out := &bytes.Buffer{}
|
|
log := New(out)
|
|
log.Log().
|
|
Str("foo", "bar").
|
|
Int("n", 123).
|
|
Msg("")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"foo":"bar","n":123}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestInfo(t *testing.T) {
|
|
t.Run("empty", func(t *testing.T) {
|
|
out := &bytes.Buffer{}
|
|
log := New(out)
|
|
log.Info().Msg("")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"info"}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
})
|
|
|
|
t.Run("one-field", func(t *testing.T) {
|
|
out := &bytes.Buffer{}
|
|
log := New(out)
|
|
log.Info().Str("foo", "bar").Msg("")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"info","foo":"bar"}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
})
|
|
|
|
t.Run("two-field", func(t *testing.T) {
|
|
out := &bytes.Buffer{}
|
|
log := New(out)
|
|
log.Info().
|
|
Str("foo", "bar").
|
|
Int("n", 123).
|
|
Msg("")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"info","foo":"bar","n":123}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestEmptyLevelFieldName(t *testing.T) {
|
|
fieldName := LevelFieldName
|
|
LevelFieldName = ""
|
|
|
|
t.Run("empty setting", func(t *testing.T) {
|
|
out := &bytes.Buffer{}
|
|
log := New(out)
|
|
log.Info().
|
|
Str("foo", "bar").
|
|
Int("n", 123).
|
|
Msg("")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"foo":"bar","n":123}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
})
|
|
LevelFieldName = fieldName
|
|
}
|
|
|
|
func TestWith(t *testing.T) {
|
|
out := &bytes.Buffer{}
|
|
ctx := New(out).With().
|
|
Str("string", "foo").
|
|
Stringer("stringer", net.IP{127, 0, 0, 1}).
|
|
Stringer("stringer_nil", nil).
|
|
Bytes("bytes", []byte("bar")).
|
|
Hex("hex", []byte{0x12, 0xef}).
|
|
RawJSON("json", []byte(`{"some":"json"}`)).
|
|
AnErr("some_err", nil).
|
|
Err(errors.New("some error")).
|
|
Bool("bool", true).
|
|
Int("int", 1).
|
|
Int8("int8", 2).
|
|
Int16("int16", 3).
|
|
Int32("int32", 4).
|
|
Int64("int64", 5).
|
|
Uint("uint", 6).
|
|
Uint8("uint8", 7).
|
|
Uint16("uint16", 8).
|
|
Uint32("uint32", 9).
|
|
Uint64("uint64", 10).
|
|
Float32("float32", 11.101).
|
|
Float64("float64", 12.30303).
|
|
Time("time", time.Time{})
|
|
_, file, line, _ := runtime.Caller(0)
|
|
caller := fmt.Sprintf("%s:%d", file, line+3)
|
|
log := ctx.Caller().Logger()
|
|
log.Log().Msg("")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"string":"foo","stringer":"127.0.0.1","stringer_nil":null,"bytes":"bar","hex":"12ef","json":{"some":"json"},"error":"some error","bool":true,"int":1,"int8":2,"int16":3,"int32":4,"int64":5,"uint":6,"uint8":7,"uint16":8,"uint32":9,"uint64":10,"float32":11.101,"float64":12.30303,"time":"0001-01-01T00:00:00Z","caller":"`+caller+`"}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
|
|
// Validate CallerWithSkipFrameCount.
|
|
out.Reset()
|
|
_, file, line, _ = runtime.Caller(0)
|
|
caller = fmt.Sprintf("%s:%d", file, line+5)
|
|
log = ctx.CallerWithSkipFrameCount(3).Logger()
|
|
func() {
|
|
log.Log().Msg("")
|
|
}()
|
|
// The above line is a little contrived, but the line above should be the line due
|
|
// to the extra frame skip.
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"string":"foo","stringer":"127.0.0.1","stringer_nil":null,"bytes":"bar","hex":"12ef","json":{"some":"json"},"error":"some error","bool":true,"int":1,"int8":2,"int16":3,"int32":4,"int64":5,"uint":6,"uint8":7,"uint16":8,"uint32":9,"uint64":10,"float32":11.101,"float64":12.30303,"time":"0001-01-01T00:00:00Z","caller":"`+caller+`"}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestFieldsMap(t *testing.T) {
|
|
out := &bytes.Buffer{}
|
|
log := New(out)
|
|
log.Log().Fields(map[string]interface{}{
|
|
"nil": nil,
|
|
"string": "foo",
|
|
"bytes": []byte("bar"),
|
|
"error": errors.New("some error"),
|
|
"bool": true,
|
|
"int": int(1),
|
|
"int8": int8(2),
|
|
"int16": int16(3),
|
|
"int32": int32(4),
|
|
"int64": int64(5),
|
|
"uint": uint(6),
|
|
"uint8": uint8(7),
|
|
"uint16": uint16(8),
|
|
"uint32": uint32(9),
|
|
"uint64": uint64(10),
|
|
"float32": float32(11),
|
|
"float64": float64(12),
|
|
"ipv6": net.IP{0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34},
|
|
"dur": 1 * time.Second,
|
|
"time": time.Time{},
|
|
"obj": obj{"a", "b", 1},
|
|
}).Msg("")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"bool":true,"bytes":"bar","dur":1000,"error":"some error","float32":11,"float64":12,"int":1,"int16":3,"int32":4,"int64":5,"int8":2,"ipv6":"2001:db8:85a3::8a2e:370:7334","nil":null,"obj":{"Pub":"a","Tag":"b","priv":1},"string":"foo","time":"0001-01-01T00:00:00Z","uint":6,"uint16":8,"uint32":9,"uint64":10,"uint8":7}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestFieldsMapPnt(t *testing.T) {
|
|
out := &bytes.Buffer{}
|
|
log := New(out)
|
|
log.Log().Fields(map[string]interface{}{
|
|
"string": new(string),
|
|
"bool": new(bool),
|
|
"int": new(int),
|
|
"int8": new(int8),
|
|
"int16": new(int16),
|
|
"int32": new(int32),
|
|
"int64": new(int64),
|
|
"uint": new(uint),
|
|
"uint8": new(uint8),
|
|
"uint16": new(uint16),
|
|
"uint32": new(uint32),
|
|
"uint64": new(uint64),
|
|
"float32": new(float32),
|
|
"float64": new(float64),
|
|
"dur": new(time.Duration),
|
|
"time": new(time.Time),
|
|
}).Msg("")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"bool":false,"dur":0,"float32":0,"float64":0,"int":0,"int16":0,"int32":0,"int64":0,"int8":0,"string":"","time":"0001-01-01T00:00:00Z","uint":0,"uint16":0,"uint32":0,"uint64":0,"uint8":0}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestFieldsMapNilPnt(t *testing.T) {
|
|
var (
|
|
stringPnt *string
|
|
boolPnt *bool
|
|
intPnt *int
|
|
int8Pnt *int8
|
|
int16Pnt *int16
|
|
int32Pnt *int32
|
|
int64Pnt *int64
|
|
uintPnt *uint
|
|
uint8Pnt *uint8
|
|
uint16Pnt *uint16
|
|
uint32Pnt *uint32
|
|
uint64Pnt *uint64
|
|
float32Pnt *float32
|
|
float64Pnt *float64
|
|
durPnt *time.Duration
|
|
timePnt *time.Time
|
|
)
|
|
out := &bytes.Buffer{}
|
|
log := New(out)
|
|
fields := map[string]interface{}{
|
|
"string": stringPnt,
|
|
"bool": boolPnt,
|
|
"int": intPnt,
|
|
"int8": int8Pnt,
|
|
"int16": int16Pnt,
|
|
"int32": int32Pnt,
|
|
"int64": int64Pnt,
|
|
"uint": uintPnt,
|
|
"uint8": uint8Pnt,
|
|
"uint16": uint16Pnt,
|
|
"uint32": uint32Pnt,
|
|
"uint64": uint64Pnt,
|
|
"float32": float32Pnt,
|
|
"float64": float64Pnt,
|
|
"dur": durPnt,
|
|
"time": timePnt,
|
|
}
|
|
|
|
log.Log().Fields(fields).Msg("")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"bool":null,"dur":null,"float32":null,"float64":null,"int":null,"int16":null,"int32":null,"int64":null,"int8":null,"string":null,"time":null,"uint":null,"uint16":null,"uint32":null,"uint64":null,"uint8":null}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestFields(t *testing.T) {
|
|
out := &bytes.Buffer{}
|
|
log := New(out)
|
|
now := time.Now()
|
|
_, file, line, _ := runtime.Caller(0)
|
|
caller := fmt.Sprintf("%s:%d", file, line+3)
|
|
log.Log().
|
|
Caller().
|
|
Str("string", "foo").
|
|
Stringer("stringer", net.IP{127, 0, 0, 1}).
|
|
Stringer("stringer_nil", nil).
|
|
Bytes("bytes", []byte("bar")).
|
|
Hex("hex", []byte{0x12, 0xef}).
|
|
RawJSON("json", []byte(`{"some":"json"}`)).
|
|
Func(func(e *Event) { e.Str("func", "func_output") }).
|
|
AnErr("some_err", nil).
|
|
Err(errors.New("some error")).
|
|
Bool("bool", true).
|
|
Int("int", 1).
|
|
Int8("int8", 2).
|
|
Int16("int16", 3).
|
|
Int32("int32", 4).
|
|
Int64("int64", 5).
|
|
Uint("uint", 6).
|
|
Uint8("uint8", 7).
|
|
Uint16("uint16", 8).
|
|
Uint32("uint32", 9).
|
|
Uint64("uint64", 10).
|
|
IPAddr("IPv4", net.IP{192, 168, 0, 100}).
|
|
IPAddr("IPv6", net.IP{0x20, 0x01, 0x0d, 0xb8, 0x85, 0xa3, 0x00, 0x00, 0x00, 0x00, 0x8a, 0x2e, 0x03, 0x70, 0x73, 0x34}).
|
|
MACAddr("Mac", net.HardwareAddr{0x00, 0x14, 0x22, 0x01, 0x23, 0x45}).
|
|
IPPrefix("Prefix", net.IPNet{IP: net.IP{192, 168, 0, 100}, Mask: net.CIDRMask(24, 32)}).
|
|
Float32("float32", 11.1234).
|
|
Float64("float64", 12.321321321).
|
|
Dur("dur", 1*time.Second).
|
|
Time("time", time.Time{}).
|
|
TimeDiff("diff", now, now.Add(-10*time.Second)).
|
|
Msg("")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"caller":"`+caller+`","string":"foo","stringer":"127.0.0.1","stringer_nil":null,"bytes":"bar","hex":"12ef","json":{"some":"json"},"func":"func_output","error":"some error","bool":true,"int":1,"int8":2,"int16":3,"int32":4,"int64":5,"uint":6,"uint8":7,"uint16":8,"uint32":9,"uint64":10,"IPv4":"192.168.0.100","IPv6":"2001:db8:85a3::8a2e:370:7334","Mac":"00:14:22:01:23:45","Prefix":"192.168.0.100/24","float32":11.1234,"float64":12.321321321,"dur":1000,"time":"0001-01-01T00:00:00Z","diff":10000}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestFieldsArrayEmpty(t *testing.T) {
|
|
out := &bytes.Buffer{}
|
|
log := New(out)
|
|
log.Log().
|
|
Strs("string", []string{}).
|
|
Errs("err", []error{}).
|
|
Bools("bool", []bool{}).
|
|
Ints("int", []int{}).
|
|
Ints8("int8", []int8{}).
|
|
Ints16("int16", []int16{}).
|
|
Ints32("int32", []int32{}).
|
|
Ints64("int64", []int64{}).
|
|
Uints("uint", []uint{}).
|
|
Uints8("uint8", []uint8{}).
|
|
Uints16("uint16", []uint16{}).
|
|
Uints32("uint32", []uint32{}).
|
|
Uints64("uint64", []uint64{}).
|
|
Floats32("float32", []float32{}).
|
|
Floats64("float64", []float64{}).
|
|
Durs("dur", []time.Duration{}).
|
|
Times("time", []time.Time{}).
|
|
Msg("")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"string":[],"err":[],"bool":[],"int":[],"int8":[],"int16":[],"int32":[],"int64":[],"uint":[],"uint8":[],"uint16":[],"uint32":[],"uint64":[],"float32":[],"float64":[],"dur":[],"time":[]}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestFieldsArraySingleElement(t *testing.T) {
|
|
out := &bytes.Buffer{}
|
|
log := New(out)
|
|
log.Log().
|
|
Strs("string", []string{"foo"}).
|
|
Errs("err", []error{errors.New("some error")}).
|
|
Bools("bool", []bool{true}).
|
|
Ints("int", []int{1}).
|
|
Ints8("int8", []int8{2}).
|
|
Ints16("int16", []int16{3}).
|
|
Ints32("int32", []int32{4}).
|
|
Ints64("int64", []int64{5}).
|
|
Uints("uint", []uint{6}).
|
|
Uints8("uint8", []uint8{7}).
|
|
Uints16("uint16", []uint16{8}).
|
|
Uints32("uint32", []uint32{9}).
|
|
Uints64("uint64", []uint64{10}).
|
|
Floats32("float32", []float32{11}).
|
|
Floats64("float64", []float64{12}).
|
|
Durs("dur", []time.Duration{1 * time.Second}).
|
|
Times("time", []time.Time{time.Time{}}).
|
|
Msg("")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"string":["foo"],"err":["some error"],"bool":[true],"int":[1],"int8":[2],"int16":[3],"int32":[4],"int64":[5],"uint":[6],"uint8":[7],"uint16":[8],"uint32":[9],"uint64":[10],"float32":[11],"float64":[12],"dur":[1000],"time":["0001-01-01T00:00:00Z"]}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestFieldsArrayMultipleElement(t *testing.T) {
|
|
out := &bytes.Buffer{}
|
|
log := New(out)
|
|
log.Log().
|
|
Strs("string", []string{"foo", "bar"}).
|
|
Errs("err", []error{errors.New("some error"), nil}).
|
|
Bools("bool", []bool{true, false}).
|
|
Ints("int", []int{1, 0}).
|
|
Ints8("int8", []int8{2, 0}).
|
|
Ints16("int16", []int16{3, 0}).
|
|
Ints32("int32", []int32{4, 0}).
|
|
Ints64("int64", []int64{5, 0}).
|
|
Uints("uint", []uint{6, 0}).
|
|
Uints8("uint8", []uint8{7, 0}).
|
|
Uints16("uint16", []uint16{8, 0}).
|
|
Uints32("uint32", []uint32{9, 0}).
|
|
Uints64("uint64", []uint64{10, 0}).
|
|
Floats32("float32", []float32{11, 0}).
|
|
Floats64("float64", []float64{12, 0}).
|
|
Durs("dur", []time.Duration{1 * time.Second, 0}).
|
|
Times("time", []time.Time{time.Time{}, time.Time{}}).
|
|
Msg("")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"string":["foo","bar"],"err":["some error",null],"bool":[true,false],"int":[1,0],"int8":[2,0],"int16":[3,0],"int32":[4,0],"int64":[5,0],"uint":[6,0],"uint8":[7,0],"uint16":[8,0],"uint32":[9,0],"uint64":[10,0],"float32":[11,0],"float64":[12,0],"dur":[1000,0],"time":["0001-01-01T00:00:00Z","0001-01-01T00:00:00Z"]}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestFieldsDisabled(t *testing.T) {
|
|
out := &bytes.Buffer{}
|
|
log := New(out).Level(InfoLevel)
|
|
now := time.Now()
|
|
log.Debug().
|
|
Str("string", "foo").
|
|
Bytes("bytes", []byte("bar")).
|
|
Hex("hex", []byte{0x12, 0xef}).
|
|
AnErr("some_err", nil).
|
|
Err(errors.New("some error")).
|
|
Func(func(e *Event) { e.Str("func", "func_output") }).
|
|
Bool("bool", true).
|
|
Int("int", 1).
|
|
Int8("int8", 2).
|
|
Int16("int16", 3).
|
|
Int32("int32", 4).
|
|
Int64("int64", 5).
|
|
Uint("uint", 6).
|
|
Uint8("uint8", 7).
|
|
Uint16("uint16", 8).
|
|
Uint32("uint32", 9).
|
|
Uint64("uint64", 10).
|
|
Float32("float32", 11).
|
|
Float64("float64", 12).
|
|
Dur("dur", 1*time.Second).
|
|
Time("time", time.Time{}).
|
|
TimeDiff("diff", now, now.Add(-10*time.Second)).
|
|
Msg("")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), ""; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestMsgf(t *testing.T) {
|
|
out := &bytes.Buffer{}
|
|
log := New(out)
|
|
log.Log().Msgf("one %s %.1f %d %v", "two", 3.4, 5, errors.New("six"))
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"message":"one two 3.4 5 six"}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestWithAndFieldsCombined(t *testing.T) {
|
|
out := &bytes.Buffer{}
|
|
log := New(out).With().Str("f1", "val").Str("f2", "val").Logger()
|
|
log.Log().Str("f3", "val").Msg("")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"f1":"val","f2":"val","f3":"val"}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestLevel(t *testing.T) {
|
|
t.Run("Disabled", func(t *testing.T) {
|
|
out := &bytes.Buffer{}
|
|
log := New(out).Level(Disabled)
|
|
log.Info().Msg("test")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), ""; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
})
|
|
|
|
t.Run("NoLevel/Disabled", func(t *testing.T) {
|
|
out := &bytes.Buffer{}
|
|
log := New(out).Level(Disabled)
|
|
log.Log().Msg("test")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), ""; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
})
|
|
|
|
t.Run("NoLevel/Info", func(t *testing.T) {
|
|
out := &bytes.Buffer{}
|
|
log := New(out).Level(InfoLevel)
|
|
log.Log().Msg("test")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"message":"test"}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
})
|
|
|
|
t.Run("NoLevel/Panic", func(t *testing.T) {
|
|
out := &bytes.Buffer{}
|
|
log := New(out).Level(PanicLevel)
|
|
log.Log().Msg("test")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"message":"test"}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
})
|
|
|
|
t.Run("NoLevel/WithLevel", func(t *testing.T) {
|
|
out := &bytes.Buffer{}
|
|
log := New(out).Level(InfoLevel)
|
|
log.WithLevel(NoLevel).Msg("test")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"message":"test"}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
})
|
|
|
|
t.Run("Info", func(t *testing.T) {
|
|
out := &bytes.Buffer{}
|
|
log := New(out).Level(InfoLevel)
|
|
log.Info().Msg("test")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"info","message":"test"}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestGetLevel(t *testing.T) {
|
|
levels := []Level{
|
|
DebugLevel,
|
|
InfoLevel,
|
|
WarnLevel,
|
|
ErrorLevel,
|
|
FatalLevel,
|
|
PanicLevel,
|
|
NoLevel,
|
|
Disabled,
|
|
}
|
|
for _, level := range levels {
|
|
if got, want := New(nil).Level(level).GetLevel(), level; got != want {
|
|
t.Errorf("GetLevel() = %v, want: %v", got, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSampling(t *testing.T) {
|
|
out := &bytes.Buffer{}
|
|
log := New(out).Sample(&BasicSampler{N: 2})
|
|
log.Log().Int("i", 1).Msg("")
|
|
log.Log().Int("i", 2).Msg("")
|
|
log.Log().Int("i", 3).Msg("")
|
|
log.Log().Int("i", 4).Msg("")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), "{\"i\":1}\n{\"i\":3}\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestDiscard(t *testing.T) {
|
|
out := &bytes.Buffer{}
|
|
log := New(out)
|
|
log.Log().Discard().Str("a", "b").Msgf("one %s %.1f %d %v", "two", 3.4, 5, errors.New("six"))
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), ""; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
|
|
// Double call
|
|
log.Log().Discard().Discard().Str("a", "b").Msgf("one %s %.1f %d %v", "two", 3.4, 5, errors.New("six"))
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), ""; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
}
|
|
|
|
type levelWriter struct {
|
|
ops []struct {
|
|
l Level
|
|
p string
|
|
}
|
|
}
|
|
|
|
func (lw *levelWriter) Write(p []byte) (int, error) {
|
|
return len(p), nil
|
|
}
|
|
|
|
func (lw *levelWriter) WriteLevel(lvl Level, p []byte) (int, error) {
|
|
p = decodeIfBinaryToBytes(p)
|
|
lw.ops = append(lw.ops, struct {
|
|
l Level
|
|
p string
|
|
}{lvl, string(p)})
|
|
return len(p), nil
|
|
}
|
|
|
|
func TestLevelWriter(t *testing.T) {
|
|
lw := &levelWriter{
|
|
ops: []struct {
|
|
l Level
|
|
p string
|
|
}{},
|
|
}
|
|
log := New(lw)
|
|
log.Trace().Msg("0")
|
|
log.Debug().Msg("1")
|
|
log.Info().Msg("2")
|
|
log.Warn().Msg("3")
|
|
log.Error().Msg("4")
|
|
log.Log().Msg("nolevel-1")
|
|
log.WithLevel(TraceLevel).Msg("5")
|
|
log.WithLevel(DebugLevel).Msg("6")
|
|
log.WithLevel(InfoLevel).Msg("7")
|
|
log.WithLevel(WarnLevel).Msg("8")
|
|
log.WithLevel(ErrorLevel).Msg("9")
|
|
log.WithLevel(NoLevel).Msg("nolevel-2")
|
|
|
|
want := []struct {
|
|
l Level
|
|
p string
|
|
}{
|
|
{TraceLevel, `{"level":"trace","message":"0"}` + "\n"},
|
|
{DebugLevel, `{"level":"debug","message":"1"}` + "\n"},
|
|
{InfoLevel, `{"level":"info","message":"2"}` + "\n"},
|
|
{WarnLevel, `{"level":"warn","message":"3"}` + "\n"},
|
|
{ErrorLevel, `{"level":"error","message":"4"}` + "\n"},
|
|
{NoLevel, `{"message":"nolevel-1"}` + "\n"},
|
|
{TraceLevel, `{"level":"trace","message":"5"}` + "\n"},
|
|
{DebugLevel, `{"level":"debug","message":"6"}` + "\n"},
|
|
{InfoLevel, `{"level":"info","message":"7"}` + "\n"},
|
|
{WarnLevel, `{"level":"warn","message":"8"}` + "\n"},
|
|
{ErrorLevel, `{"level":"error","message":"9"}` + "\n"},
|
|
{NoLevel, `{"message":"nolevel-2"}` + "\n"},
|
|
}
|
|
if got := lw.ops; !reflect.DeepEqual(got, want) {
|
|
t.Errorf("invalid ops:\ngot:\n%v\nwant:\n%v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestContextTimestamp(t *testing.T) {
|
|
TimestampFunc = func() time.Time {
|
|
return time.Date(2001, time.February, 3, 4, 5, 6, 7, time.UTC)
|
|
}
|
|
defer func() {
|
|
TimestampFunc = time.Now
|
|
}()
|
|
out := &bytes.Buffer{}
|
|
log := New(out).With().Timestamp().Str("foo", "bar").Logger()
|
|
log.Log().Msg("hello world")
|
|
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"foo":"bar","time":"2001-02-03T04:05:06Z","message":"hello world"}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestEventTimestamp(t *testing.T) {
|
|
TimestampFunc = func() time.Time {
|
|
return time.Date(2001, time.February, 3, 4, 5, 6, 7, time.UTC)
|
|
}
|
|
defer func() {
|
|
TimestampFunc = time.Now
|
|
}()
|
|
out := &bytes.Buffer{}
|
|
log := New(out).With().Str("foo", "bar").Logger()
|
|
log.Log().Timestamp().Msg("hello world")
|
|
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"foo":"bar","time":"2001-02-03T04:05:06Z","message":"hello world"}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestOutputWithoutTimestamp(t *testing.T) {
|
|
ignoredOut := &bytes.Buffer{}
|
|
out := &bytes.Buffer{}
|
|
log := New(ignoredOut).Output(out).With().Str("foo", "bar").Logger()
|
|
log.Log().Msg("hello world")
|
|
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"foo":"bar","message":"hello world"}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestOutputWithTimestamp(t *testing.T) {
|
|
TimestampFunc = func() time.Time {
|
|
return time.Date(2001, time.February, 3, 4, 5, 6, 7, time.UTC)
|
|
}
|
|
defer func() {
|
|
TimestampFunc = time.Now
|
|
}()
|
|
ignoredOut := &bytes.Buffer{}
|
|
out := &bytes.Buffer{}
|
|
log := New(ignoredOut).Output(out).With().Timestamp().Str("foo", "bar").Logger()
|
|
log.Log().Msg("hello world")
|
|
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"foo":"bar","time":"2001-02-03T04:05:06Z","message":"hello world"}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
}
|
|
|
|
type loggableError struct {
|
|
error
|
|
}
|
|
|
|
func (l loggableError) MarshalZerologObject(e *Event) {
|
|
e.Str("message", l.error.Error()+": loggableError")
|
|
}
|
|
|
|
func TestErrorMarshalFunc(t *testing.T) {
|
|
out := &bytes.Buffer{}
|
|
log := New(out)
|
|
|
|
// test default behaviour
|
|
log.Log().Err(errors.New("err")).Msg("msg")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"error":"err","message":"msg"}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
out.Reset()
|
|
|
|
log.Log().Err(loggableError{errors.New("err")}).Msg("msg")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"error":{"message":"err: loggableError"},"message":"msg"}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
out.Reset()
|
|
|
|
// test overriding the ErrorMarshalFunc
|
|
originalErrorMarshalFunc := ErrorMarshalFunc
|
|
defer func() {
|
|
ErrorMarshalFunc = originalErrorMarshalFunc
|
|
}()
|
|
|
|
ErrorMarshalFunc = func(err error) interface{} {
|
|
return err.Error() + ": marshaled string"
|
|
}
|
|
log.Log().Err(errors.New("err")).Msg("msg")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"error":"err: marshaled string","message":"msg"}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
|
|
out.Reset()
|
|
ErrorMarshalFunc = func(err error) interface{} {
|
|
return errors.New(err.Error() + ": new error")
|
|
}
|
|
log.Log().Err(errors.New("err")).Msg("msg")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"error":"err: new error","message":"msg"}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
|
|
out.Reset()
|
|
ErrorMarshalFunc = func(err error) interface{} {
|
|
return loggableError{err}
|
|
}
|
|
log.Log().Err(errors.New("err")).Msg("msg")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"error":{"message":"err: loggableError"},"message":"msg"}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestCallerMarshalFunc(t *testing.T) {
|
|
out := &bytes.Buffer{}
|
|
log := New(out)
|
|
|
|
// test default behaviour this is really brittle due to the line numbers
|
|
// actually mattering for validation
|
|
_, file, line, _ := runtime.Caller(0)
|
|
caller := fmt.Sprintf("%s:%d", file, line+2)
|
|
log.Log().Caller().Msg("msg")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"caller":"`+caller+`","message":"msg"}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
out.Reset()
|
|
|
|
// test custom behavior. In this case we'll take just the last directory
|
|
origCallerMarshalFunc := CallerMarshalFunc
|
|
defer func() { CallerMarshalFunc = origCallerMarshalFunc }()
|
|
CallerMarshalFunc = func(file string, line int) string {
|
|
parts := strings.Split(file, "/")
|
|
if len(parts) > 1 {
|
|
return strings.Join(parts[len(parts)-2:], "/") + ":" + strconv.Itoa(line)
|
|
}
|
|
|
|
return file + ":" + strconv.Itoa(line)
|
|
}
|
|
_, file, line, _ = runtime.Caller(0)
|
|
caller = CallerMarshalFunc(file, line+2)
|
|
log.Log().Caller().Msg("msg")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"caller":"`+caller+`","message":"msg"}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestLevelFieldMarshalFunc(t *testing.T) {
|
|
origLevelFieldMarshalFunc := LevelFieldMarshalFunc
|
|
LevelFieldMarshalFunc = func(l Level) string {
|
|
return strings.ToUpper(l.String())
|
|
}
|
|
defer func() {
|
|
LevelFieldMarshalFunc = origLevelFieldMarshalFunc
|
|
}()
|
|
out := &bytes.Buffer{}
|
|
log := New(out)
|
|
|
|
log.Debug().Msg("test")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"DEBUG","message":"test"}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
out.Reset()
|
|
|
|
log.Info().Msg("test")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"INFO","message":"test"}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
out.Reset()
|
|
|
|
log.Warn().Msg("test")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"WARN","message":"test"}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
out.Reset()
|
|
|
|
log.Error().Msg("test")
|
|
if got, want := decodeIfBinaryToString(out.Bytes()), `{"level":"ERROR","message":"test"}`+"\n"; got != want {
|
|
t.Errorf("invalid log output:\ngot: %v\nwant: %v", got, want)
|
|
}
|
|
out.Reset()
|
|
}
|
|
|
|
type errWriter struct {
|
|
error
|
|
}
|
|
|
|
func (w errWriter) Write(p []byte) (n int, err error) {
|
|
return 0, w.error
|
|
}
|
|
|
|
func TestErrorHandler(t *testing.T) {
|
|
var got error
|
|
want := errors.New("write error")
|
|
ErrorHandler = func(err error) {
|
|
got = err
|
|
}
|
|
log := New(errWriter{want})
|
|
log.Log().Msg("test")
|
|
if got != want {
|
|
t.Errorf("ErrorHandler err = %#v, want %#v", got, want)
|
|
}
|
|
}
|
|
|
|
func TestUpdateEmptyContext(t *testing.T) {
|
|
var buf bytes.Buffer
|
|
log := New(&buf)
|
|
|
|
log.UpdateContext(func(c Context) Context {
|
|
return c.Str("foo", "bar")
|
|
})
|
|
log.Info().Msg("no panic")
|
|
|
|
want := `{"level":"info","foo":"bar","message":"no panic"}` + "\n"
|
|
|
|
if got := decodeIfBinaryToString(buf.Bytes()); got != want {
|
|
t.Errorf("invalid log output:\ngot: %q\nwant: %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestLevel_String(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
l Level
|
|
want string
|
|
}{
|
|
{"trace", TraceLevel, "trace"},
|
|
{"debug", DebugLevel, "debug"},
|
|
{"info", InfoLevel, "info"},
|
|
{"warn", WarnLevel, "warn"},
|
|
{"error", ErrorLevel, "error"},
|
|
{"fatal", FatalLevel, "fatal"},
|
|
{"panic", PanicLevel, "panic"},
|
|
{"disabled", Disabled, "disabled"},
|
|
{"nolevel", NoLevel, ""},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if got := tt.l.String(); got != tt.want {
|
|
t.Errorf("String() = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestParseLevel(t *testing.T) {
|
|
type args struct {
|
|
levelStr string
|
|
}
|
|
tests := []struct {
|
|
name string
|
|
args args
|
|
want Level
|
|
wantErr bool
|
|
}{
|
|
{"trace", args{"trace"}, TraceLevel, false},
|
|
{"debug", args{"debug"}, DebugLevel, false},
|
|
{"info", args{"info"}, InfoLevel, false},
|
|
{"warn", args{"warn"}, WarnLevel, false},
|
|
{"error", args{"error"}, ErrorLevel, false},
|
|
{"fatal", args{"fatal"}, FatalLevel, false},
|
|
{"panic", args{"panic"}, PanicLevel, false},
|
|
{"disabled", args{"disabled"}, Disabled, false},
|
|
{"nolevel", args{""}, NoLevel, false},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
got, err := ParseLevel(tt.args.levelStr)
|
|
if (err != nil) != tt.wantErr {
|
|
t.Errorf("ParseLevel() error = %v, wantErr %v", err, tt.wantErr)
|
|
return
|
|
}
|
|
if got != tt.want {
|
|
t.Errorf("ParseLevel() got = %v, want %v", got, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|