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 }