diff --git a/error.go b/error.go index 65982a51e..db0002d7e 100644 --- a/error.go +++ b/error.go @@ -21,9 +21,8 @@ package zap import ( - "sync" - "go.uber.org/zap/zapcore" + "sync" ) var _errArrayElemPool = sync.Pool{New: func() interface{} { diff --git a/zapcore/error.go b/zapcore/error.go index 9ba2272c3..84ad4114b 100644 --- a/zapcore/error.go +++ b/zapcore/error.go @@ -22,6 +22,7 @@ package zapcore import ( "fmt" + "reflect" "sync" ) @@ -42,13 +43,29 @@ import ( // ... // ], // } -func encodeError(key string, err error, enc ObjectEncoder) error { +func encodeError(key string, err error, enc ObjectEncoder) (retErr error) { + // Try to capture panics (from nil references or otherwise) when calling + // the Error() method + defer func() { + if err := recover(); err != nil { + // If it's a nil pointer, just say "". The likeliest causes are a + // Stringer that fails to guard against nil or a nil pointer for a + // value receiver, and in either case, "" is a nice result. + if v := reflect.ValueOf(err); v.Kind() == reflect.Ptr && v.IsNil() { + enc.AddString(key, "") + return + } + + retErr = fmt.Errorf("PANIC=%v", err) + } + }() + basic := err.Error() enc.AddString(key, basic) switch e := err.(type) { case errorGroup: - return enc.AddArray(key+"Causes", errArray(e.Errors())) + retErr = enc.AddArray(key+"Causes", errArray(e.Errors())) case fmt.Formatter: verbose := fmt.Sprintf("%+v", e) if verbose != basic { @@ -57,7 +74,7 @@ func encodeError(key string, err error, enc ObjectEncoder) error { enc.AddString(key+"Verbose", verbose) } } - return nil + return retErr } type errorGroup interface { diff --git a/zapcore/field.go b/zapcore/field.go index 7e255d63e..e0105868e 100644 --- a/zapcore/field.go +++ b/zapcore/field.go @@ -167,7 +167,7 @@ func (f Field) AddTo(enc ObjectEncoder) { case StringerType: err = encodeStringer(f.Key, f.Interface, enc) case ErrorType: - encodeError(f.Key, f.Interface.(error), enc) + err = encodeError(f.Key, f.Interface.(error), enc) case SkipType: break default: