...

Source file src/go.uber.org/zap/zapcore/error.go

Documentation: go.uber.org/zap/zapcore

     1  // Copyright (c) 2017 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package zapcore
    22  
    23  import (
    24  	"fmt"
    25  	"reflect"
    26  
    27  	"go.uber.org/zap/internal/pool"
    28  )
    29  
    30  // Encodes the given error into fields of an object. A field with the given
    31  // name is added for the error message.
    32  //
    33  // If the error implements fmt.Formatter, a field with the name ${key}Verbose
    34  // is also added with the full verbose error message.
    35  //
    36  // Finally, if the error implements errorGroup (from go.uber.org/multierr) or
    37  // causer (from github.com/pkg/errors), a ${key}Causes field is added with an
    38  // array of objects containing the errors this error was comprised of.
    39  //
    40  //	{
    41  //	  "error": err.Error(),
    42  //	  "errorVerbose": fmt.Sprintf("%+v", err),
    43  //	  "errorCauses": [
    44  //	    ...
    45  //	  ],
    46  //	}
    47  func encodeError(key string, err error, enc ObjectEncoder) (retErr error) {
    48  	// Try to capture panics (from nil references or otherwise) when calling
    49  	// the Error() method
    50  	defer func() {
    51  		if rerr := recover(); rerr != nil {
    52  			// If it's a nil pointer, just say "<nil>". The likeliest causes are a
    53  			// error that fails to guard against nil or a nil pointer for a
    54  			// value receiver, and in either case, "<nil>" is a nice result.
    55  			if v := reflect.ValueOf(err); v.Kind() == reflect.Ptr && v.IsNil() {
    56  				enc.AddString(key, "<nil>")
    57  				return
    58  			}
    59  
    60  			retErr = fmt.Errorf("PANIC=%v", rerr)
    61  		}
    62  	}()
    63  
    64  	basic := err.Error()
    65  	enc.AddString(key, basic)
    66  
    67  	switch e := err.(type) {
    68  	case errorGroup:
    69  		return enc.AddArray(key+"Causes", errArray(e.Errors()))
    70  	case fmt.Formatter:
    71  		verbose := fmt.Sprintf("%+v", e)
    72  		if verbose != basic {
    73  			// This is a rich error type, like those produced by
    74  			// github.com/pkg/errors.
    75  			enc.AddString(key+"Verbose", verbose)
    76  		}
    77  	}
    78  	return nil
    79  }
    80  
    81  type errorGroup interface {
    82  	// Provides read-only access to the underlying list of errors, preferably
    83  	// without causing any allocs.
    84  	Errors() []error
    85  }
    86  
    87  // Note that errArray and errArrayElem are very similar to the version
    88  // implemented in the top-level error.go file. We can't re-use this because
    89  // that would require exporting errArray as part of the zapcore API.
    90  
    91  // Encodes a list of errors using the standard error encoding logic.
    92  type errArray []error
    93  
    94  func (errs errArray) MarshalLogArray(arr ArrayEncoder) error {
    95  	for i := range errs {
    96  		if errs[i] == nil {
    97  			continue
    98  		}
    99  
   100  		el := newErrArrayElem(errs[i])
   101  		err := arr.AppendObject(el)
   102  		el.Free()
   103  		if err != nil {
   104  			return err
   105  		}
   106  	}
   107  	return nil
   108  }
   109  
   110  var _errArrayElemPool = pool.New(func() *errArrayElem {
   111  	return &errArrayElem{}
   112  })
   113  
   114  // Encodes any error into a {"error": ...} re-using the same errors logic.
   115  //
   116  // May be passed in place of an array to build a single-element array.
   117  type errArrayElem struct{ err error }
   118  
   119  func newErrArrayElem(err error) *errArrayElem {
   120  	e := _errArrayElemPool.Get()
   121  	e.err = err
   122  	return e
   123  }
   124  
   125  func (e *errArrayElem) MarshalLogArray(arr ArrayEncoder) error {
   126  	return arr.AppendObject(e)
   127  }
   128  
   129  func (e *errArrayElem) MarshalLogObject(enc ObjectEncoder) error {
   130  	return encodeError("error", e.err, enc)
   131  }
   132  
   133  func (e *errArrayElem) Free() {
   134  	e.err = nil
   135  	_errArrayElemPool.Put(e)
   136  }
   137  

View as plain text