github.com/newrelic/go-agent@v3.26.0+incompatible/_integrations/nrpkgerrors/nrpkgerrors.go (about)

     1  // Copyright 2020 New Relic Corporation. All rights reserved.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  // Package nrpkgerrors introduces support for https://github.com/pkg/errors.
     5  //
     6  // This package improves the class and stack-trace fields of pkg/error errors
     7  // when they are recorded with Transaction.NoticeError.
     8  //
     9  package nrpkgerrors
    10  
    11  import (
    12  	"fmt"
    13  
    14  	newrelic "github.com/newrelic/go-agent"
    15  	"github.com/newrelic/go-agent/internal"
    16  	"github.com/pkg/errors"
    17  )
    18  
    19  func init() { internal.TrackUsage("integration", "pkg-errors") }
    20  
    21  type nrpkgerror struct {
    22  	error
    23  }
    24  
    25  // stackTracer is an error that also knows about its StackTrace.
    26  // All wrapped errors from github.com/pkg/errors implement this interface.
    27  type stackTracer interface {
    28  	StackTrace() errors.StackTrace
    29  }
    30  
    31  func deepestStackTrace(err error) errors.StackTrace {
    32  	var last stackTracer
    33  	for err != nil {
    34  		if err, ok := err.(stackTracer); ok {
    35  			last = err
    36  		}
    37  		cause, ok := err.(interface {
    38  			Cause() error
    39  		})
    40  		if !ok {
    41  			break
    42  		}
    43  		err = cause.Cause()
    44  	}
    45  
    46  	if last == nil {
    47  		return nil
    48  	}
    49  	return last.StackTrace()
    50  }
    51  
    52  func transformStackTrace(orig errors.StackTrace) []uintptr {
    53  	st := make([]uintptr, len(orig))
    54  	for i, frame := range orig {
    55  		st[i] = uintptr(frame)
    56  	}
    57  	return st
    58  }
    59  
    60  func (e nrpkgerror) StackTrace() []uintptr {
    61  	st := deepestStackTrace(e.error)
    62  	if nil == st {
    63  		return nil
    64  	}
    65  	return transformStackTrace(st)
    66  }
    67  
    68  func (e nrpkgerror) ErrorClass() string {
    69  	if ec, ok := e.error.(newrelic.ErrorClasser); ok {
    70  		return ec.ErrorClass()
    71  	}
    72  	cause := errors.Cause(e.error)
    73  	if ec, ok := cause.(newrelic.ErrorClasser); ok {
    74  		return ec.ErrorClass()
    75  	}
    76  	return fmt.Sprintf("%T", cause)
    77  }
    78  
    79  // Wrap wraps a pkg/errors error so that when noticed by
    80  // newrelic.Transaction.NoticeError it gives an improved stacktrace and class
    81  // type.
    82  func Wrap(e error) error {
    83  	return nrpkgerror{e}
    84  }