diff --git a/array.go b/array.go index d88c497..0f7f53e 100644 --- a/array.go +++ b/array.go @@ -93,8 +93,7 @@ func (a *Array) RawJSON(val []byte) *Array { // Err serializes and appends the err to the array. func (a *Array) Err(err error) *Array { - marshaled := ErrorMarshalFunc(err) - switch m := marshaled.(type) { + switch m := ErrorMarshalFunc(err).(type) { case LogObjectMarshaler: e := newEvent(nil, 0) e.buf = e.buf[:0] @@ -102,7 +101,11 @@ func (a *Array) Err(err error) *Array { a.buf = append(enc.AppendArrayDelim(a.buf), e.buf...) putEvent(e) case error: - a.buf = enc.AppendString(enc.AppendArrayDelim(a.buf), m.Error()) + if m == nil || isNilValue(m) { + a.buf = enc.AppendNil(enc.AppendArrayDelim(a.buf)) + } else { + a.buf = enc.AppendString(enc.AppendArrayDelim(a.buf), m.Error()) + } case string: a.buf = enc.AppendString(enc.AppendArrayDelim(a.buf), m) default: diff --git a/context.go b/context.go index 1815a08..27f0d9a 100644 --- a/context.go +++ b/context.go @@ -104,14 +104,17 @@ func (c Context) RawJSON(key string, b []byte) Context { // AnErr adds the field key with serialized err to the logger context. func (c Context) AnErr(key string, err error) Context { - marshaled := ErrorMarshalFunc(err) - switch m := marshaled.(type) { + switch m := ErrorMarshalFunc(err).(type) { case nil: return c case LogObjectMarshaler: return c.Object(key, m) case error: - return c.Str(key, m.Error()) + if m == nil || isNilValue(m) { + return c + } else { + return c.Str(key, m.Error()) + } case string: return c.Str(key, m) default: @@ -124,12 +127,15 @@ func (c Context) AnErr(key string, err error) Context { func (c Context) Errs(key string, errs []error) Context { arr := Arr() for _, err := range errs { - marshaled := ErrorMarshalFunc(err) - switch m := marshaled.(type) { + switch m := ErrorMarshalFunc(err).(type) { case LogObjectMarshaler: arr = arr.Object(m) case error: - arr = arr.Str(m.Error()) + if m == nil || isNilValue(m) { + arr = arr.Interface(nil) + } else { + arr = arr.Str(m.Error()) + } case string: arr = arr.Str(m) default: diff --git a/event.go b/event.go index 1402835..224799c 100644 --- a/event.go +++ b/event.go @@ -280,7 +280,11 @@ func (e *Event) AnErr(key string, err error) *Event { case LogObjectMarshaler: return e.Object(key, m) case error: - return e.Str(key, m.Error()) + if m == nil || isNilValue(m) { + return e + } else { + return e.Str(key, m.Error()) + } case string: return e.Str(key, m) default: @@ -330,7 +334,9 @@ func (e *Event) Err(err error) *Event { case LogObjectMarshaler: e.Object(ErrorStackFieldName, m) case error: - e.Str(ErrorStackFieldName, m.Error()) + if m != nil && !isNilValue(m) { + e.Str(ErrorStackFieldName, m.Error()) + } case string: e.Str(ErrorStackFieldName, m) default: diff --git a/event_test.go b/event_test.go new file mode 100644 index 0000000..879eca1 --- /dev/null +++ b/event_test.go @@ -0,0 +1,39 @@ +// +build !binary_log + +package zerolog + +import ( + "bytes" + "errors" + "strings" + "testing" +) + +type nilError struct{} + +func (nilError) Error() string { + return "" +} + +func TestEvent_AnErr(t *testing.T) { + tests := []struct { + name string + err error + want string + }{ + {"nil", nil, `{}`}, + {"error", errors.New("test"), `{"err":"test"}`}, + {"nil interface", func() *nilError { return nil }(), `{}`}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + var buf bytes.Buffer + e := newEvent(levelWriterAdapter{&buf}, DebugLevel) + e.AnErr("err", tt.err) + _ = e.write() + if got, want := strings.TrimSpace(buf.String()), tt.want; got != want { + t.Errorf("Event.AnErr() = %v, want %v", got, want) + } + }) + } +} diff --git a/fields.go b/fields.go index 6b62ecc..cf3c3e9 100644 --- a/fields.go +++ b/fields.go @@ -4,8 +4,13 @@ import ( "net" "sort" "time" + "unsafe" ) +func isNilValue(i interface{}) bool { + return (*[2]uintptr)(unsafe.Pointer(&i))[1] == 0 +} + func appendFields(dst []byte, fields map[string]interface{}) []byte { keys := make([]string, 0, len(fields)) for key := range fields { @@ -29,8 +34,7 @@ func appendFields(dst []byte, fields map[string]interface{}) []byte { case []byte: dst = enc.AppendBytes(dst, val) case error: - marshaled := ErrorMarshalFunc(val) - switch m := marshaled.(type) { + switch m := ErrorMarshalFunc(val).(type) { case LogObjectMarshaler: e := newEvent(nil, 0) e.buf = e.buf[:0] @@ -38,7 +42,11 @@ func appendFields(dst []byte, fields map[string]interface{}) []byte { dst = append(dst, e.buf...) putEvent(e) case error: - dst = enc.AppendString(dst, m.Error()) + if m == nil || isNilValue(m) { + dst = enc.AppendNil(dst) + } else { + dst = enc.AppendString(dst, m.Error()) + } case string: dst = enc.AppendString(dst, m) default: @@ -47,8 +55,7 @@ func appendFields(dst []byte, fields map[string]interface{}) []byte { case []error: dst = enc.AppendArrayStart(dst) for i, err := range val { - marshaled := ErrorMarshalFunc(err) - switch m := marshaled.(type) { + switch m := ErrorMarshalFunc(err).(type) { case LogObjectMarshaler: e := newEvent(nil, 0) e.buf = e.buf[:0] @@ -56,7 +63,11 @@ func appendFields(dst []byte, fields map[string]interface{}) []byte { dst = append(dst, e.buf...) putEvent(e) case error: - dst = enc.AppendString(dst, m.Error()) + if m == nil || isNilValue(m) { + dst = enc.AppendNil(dst) + } else { + dst = enc.AppendString(dst, m.Error()) + } case string: dst = enc.AppendString(dst, m) default: