Skip to content

Commit

Permalink
Support rich errors in Any and Errors
Browse files Browse the repository at this point in the history
  • Loading branch information
Akshay Shah committed Feb 23, 2017
1 parent c555037 commit 4a5cbda
Show file tree
Hide file tree
Showing 4 changed files with 33 additions and 13 deletions.
12 changes: 11 additions & 1 deletion array.go
Original file line number Diff line number Diff line change
Expand Up @@ -311,7 +311,17 @@ func (errs errArray) MarshalLogArray(arr zapcore.ArrayEncoder) error {
if errs[i] == nil {
continue
}
arr.AppendString(errs[i].Error())
arr.AppendObject(errArrayElem{errs[i]})
}
return nil
}

type errArrayElem struct {
error
}

func (e errArrayElem) MarshalLogObject(enc zapcore.ObjectEncoder) error {
Error(e.error).AddTo(enc)
return nil

}
6 changes: 5 additions & 1 deletion array_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,11 @@ func TestArrayWrappers(t *testing.T) {
{"uint16s", Uint16s("", []uint16{1, 2}), []interface{}{uint16(1), uint16(2)}},
{"uint8s", Uint8s("", []uint8{1, 2}), []interface{}{uint8(1), uint8(2)}},
{"uintptrs", Uintptrs("", []uintptr{1, 2}), []interface{}{uintptr(1), uintptr(2)}},
{"errors", Errors("", []error{nil, errors.New("foo"), nil, errors.New("bar")}), []interface{}{"foo", "bar"}},
{
"errors",
Errors("", []error{nil, errors.New("foo"), nil, errors.New("bar")}),
[]interface{}{map[string]interface{}{"error": "foo"}, map[string]interface{}{"error": "bar"}},
},
}

for _, tt := range tests {
Expand Down
24 changes: 14 additions & 10 deletions field.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,19 +171,23 @@ func Time(key string, val time.Time) zapcore.Field {
return zapcore.Field{Key: key, Type: zapcore.TimeType, Integer: val.UnixNano()}
}

// Error constructs a field that lazily stores err.Error() under the key
// "error". If passed a nil error, the field is a no-op. This is purely a
// convenience for a common error-logging idiom; use String("someFieldName",
// err.Error()) to customize the key.
//
// Errors which also implement fmt.Formatter (like those produced by
// github.com/pkg/errors) will also have their verbose representation stored
// under "errorVerbose".
// Error is shorthand for the common idiom NamedError("error", err).
func Error(err error) zapcore.Field {
return NamedError("error", err)
}

// NamedError constructs a field that lazily stores err.Error() under the
// provided key. Errors which also implement fmt.Formatter (like those produced
// by github.com/pkg/errors) will also have their verbose representation stored
// under key+"Verbose". If passed a nil error, the field is a no-op.
//
// For the common case in which the key is simply "error", the Error function
// is shorter and less repetitive.
func NamedError(key string, err error) zapcore.Field {
if err == nil {
return Skip()
}
return zapcore.Field{Key: "error", Type: zapcore.ErrorType, Interface: err}
return zapcore.Field{Key: key, Type: zapcore.ErrorType, Interface: err}
}

// Stack constructs a field that stores a stacktrace of the current goroutine
Expand Down Expand Up @@ -303,7 +307,7 @@ func Any(key string, value interface{}) zapcore.Field {
case []time.Duration:
return Durations(key, val)
case error:
return String(key, val.Error())
return NamedError(key, val)
case []error:
return Errors(key, val)
case fmt.Stringer:
Expand Down
4 changes: 3 additions & 1 deletion field_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,12 +100,14 @@ func TestFieldConstructors(t *testing.T) {
{"Reflect", zapcore.Field{Key: "k", Type: zapcore.ReflectType, Interface: ints}, Reflect("k", ints)},
{"Error", Skip(), Error(nil)},
{"Error", zapcore.Field{Key: "error", Type: zapcore.ErrorType, Interface: fail}, Error(fail)},
{"NamedError", Skip(), NamedError("foo", nil)},
{"NamedError", zapcore.Field{Key: "foo", Type: zapcore.ErrorType, Interface: fail}, NamedError("foo", fail)},
{"Stringer", zapcore.Field{Key: "k", Type: zapcore.StringerType, Interface: addr}, Stringer("k", addr)},
{"Object", zapcore.Field{Key: "k", Type: zapcore.ObjectMarshalerType, Interface: name}, Object("k", name)},
{"Any:ObjectMarshaler", Any("k", name), Object("k", name)},
{"Any:ArrayMarshaler", Any("k", bools([]bool{true})), Array("k", bools([]bool{true}))},
{"Any:Stringer", Any("k", addr), Stringer("k", addr)},
{"Any:Error", Any("k", errors.New("v")), String("k", "v")},
{"Any:Error", Any("k", errors.New("v")), NamedError("k", errors.New("v"))},
{"Any:Errors", Any("k", []error{errors.New("v")}), Errors("k", []error{errors.New("v")})},
{"Any:Bool", Any("k", true), Bool("k", true)},
{"Any:Bools", Any("k", []bool{true}), Bools("k", []bool{true})},
Expand Down

0 comments on commit 4a5cbda

Please sign in to comment.