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

     1  // Copyright 2020 New Relic Corporation. All rights reserved.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package internal
     5  
     6  import (
     7  	"bytes"
     8  	"fmt"
     9  	"net/http"
    10  	"strconv"
    11  	"time"
    12  
    13  	"github.com/newrelic/go-agent/internal/jsonx"
    14  )
    15  
    16  const (
    17  	// PanicErrorKlass is the error klass used for errors generated by
    18  	// recovering panics in txn.End.
    19  	PanicErrorKlass = "panic"
    20  )
    21  
    22  func panicValueMsg(v interface{}) string {
    23  	switch val := v.(type) {
    24  	case error:
    25  		return val.Error()
    26  	default:
    27  		return fmt.Sprintf("%v", v)
    28  	}
    29  }
    30  
    31  // TxnErrorFromPanic creates a new TxnError from a panic.
    32  func TxnErrorFromPanic(now time.Time, v interface{}) ErrorData {
    33  	return ErrorData{
    34  		When:  now,
    35  		Msg:   panicValueMsg(v),
    36  		Klass: PanicErrorKlass,
    37  	}
    38  }
    39  
    40  // TxnErrorFromResponseCode creates a new TxnError from an http response code.
    41  func TxnErrorFromResponseCode(now time.Time, code int) ErrorData {
    42  	codeStr := strconv.Itoa(code)
    43  	msg := http.StatusText(code)
    44  	if msg == "" {
    45  		// Use a generic message if the code was not an http code
    46  		// to support gRPC.
    47  		msg = "response code " + codeStr
    48  	}
    49  	return ErrorData{
    50  		When:  now,
    51  		Msg:   msg,
    52  		Klass: codeStr,
    53  	}
    54  }
    55  
    56  // ErrorData contains the information about a recorded error.
    57  type ErrorData struct {
    58  	When            time.Time
    59  	Stack           StackTrace
    60  	ExtraAttributes map[string]interface{}
    61  	Msg             string
    62  	Klass           string
    63  }
    64  
    65  // TxnError combines error data with information about a transaction.  TxnError is used for
    66  // both error events and traced errors.
    67  type TxnError struct {
    68  	ErrorData
    69  	TxnEvent
    70  }
    71  
    72  // ErrorEvent and tracedError are separate types so that error events and traced errors can have
    73  // different WriteJSON methods.
    74  type ErrorEvent TxnError
    75  
    76  type tracedError TxnError
    77  
    78  // TxnErrors is a set of errors captured in a Transaction.
    79  type TxnErrors []*ErrorData
    80  
    81  // NewTxnErrors returns a new empty TxnErrors.
    82  func NewTxnErrors(max int) TxnErrors {
    83  	return make([]*ErrorData, 0, max)
    84  }
    85  
    86  // Add adds a TxnError.
    87  func (errors *TxnErrors) Add(e ErrorData) {
    88  	if len(*errors) < cap(*errors) {
    89  		*errors = append(*errors, &e)
    90  	}
    91  }
    92  
    93  func (h *tracedError) WriteJSON(buf *bytes.Buffer) {
    94  	buf.WriteByte('[')
    95  	jsonx.AppendFloat(buf, timeToFloatMilliseconds(h.When))
    96  	buf.WriteByte(',')
    97  	jsonx.AppendString(buf, h.FinalName)
    98  	buf.WriteByte(',')
    99  	jsonx.AppendString(buf, h.Msg)
   100  	buf.WriteByte(',')
   101  	jsonx.AppendString(buf, h.Klass)
   102  	buf.WriteByte(',')
   103  
   104  	buf.WriteByte('{')
   105  	buf.WriteString(`"agentAttributes"`)
   106  	buf.WriteByte(':')
   107  	agentAttributesJSON(h.Attrs, buf, destError)
   108  	buf.WriteByte(',')
   109  	buf.WriteString(`"userAttributes"`)
   110  	buf.WriteByte(':')
   111  	userAttributesJSON(h.Attrs, buf, destError, h.ErrorData.ExtraAttributes)
   112  	buf.WriteByte(',')
   113  	buf.WriteString(`"intrinsics"`)
   114  	buf.WriteByte(':')
   115  	intrinsicsJSON(&h.TxnEvent, buf)
   116  	if nil != h.Stack {
   117  		buf.WriteByte(',')
   118  		buf.WriteString(`"stack_trace"`)
   119  		buf.WriteByte(':')
   120  		h.Stack.WriteJSON(buf)
   121  	}
   122  	buf.WriteByte('}')
   123  
   124  	buf.WriteByte(']')
   125  }
   126  
   127  // MarshalJSON is used for testing.
   128  func (h *tracedError) MarshalJSON() ([]byte, error) {
   129  	buf := &bytes.Buffer{}
   130  	h.WriteJSON(buf)
   131  	return buf.Bytes(), nil
   132  }
   133  
   134  type harvestErrors []*tracedError
   135  
   136  func newHarvestErrors(max int) harvestErrors {
   137  	return make([]*tracedError, 0, max)
   138  }
   139  
   140  // MergeTxnErrors merges a transaction's errors into the harvest's errors.
   141  func MergeTxnErrors(errors *harvestErrors, errs TxnErrors, txnEvent TxnEvent) {
   142  	for _, e := range errs {
   143  		if len(*errors) == cap(*errors) {
   144  			return
   145  		}
   146  		*errors = append(*errors, &tracedError{
   147  			TxnEvent:  txnEvent,
   148  			ErrorData: *e,
   149  		})
   150  	}
   151  }
   152  
   153  func (errors harvestErrors) Data(agentRunID string, harvestStart time.Time) ([]byte, error) {
   154  	if 0 == len(errors) {
   155  		return nil, nil
   156  	}
   157  	estimate := 1024 * len(errors)
   158  	buf := bytes.NewBuffer(make([]byte, 0, estimate))
   159  	buf.WriteByte('[')
   160  	jsonx.AppendString(buf, agentRunID)
   161  	buf.WriteByte(',')
   162  	buf.WriteByte('[')
   163  	for i, e := range errors {
   164  		if i > 0 {
   165  			buf.WriteByte(',')
   166  		}
   167  		e.WriteJSON(buf)
   168  	}
   169  	buf.WriteByte(']')
   170  	buf.WriteByte(']')
   171  	return buf.Bytes(), nil
   172  }
   173  
   174  func (errors harvestErrors) MergeIntoHarvest(h *Harvest) {}
   175  
   176  func (errors harvestErrors) EndpointMethod() string {
   177  	return cmdErrorData
   178  }