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