github.com/newrelic/go-agent@v3.26.0+incompatible/internal/custom_event.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 "regexp" 10 "time" 11 ) 12 13 // https://newrelic.atlassian.net/wiki/display/eng/Custom+Events+in+New+Relic+Agents 14 15 var ( 16 eventTypeRegexRaw = `^[a-zA-Z0-9:_ ]+$` 17 eventTypeRegex = regexp.MustCompile(eventTypeRegexRaw) 18 19 errEventTypeLength = fmt.Errorf("event type exceeds length limit of %d", 20 attributeKeyLengthLimit) 21 // ErrEventTypeRegex will be returned to caller of app.RecordCustomEvent 22 // if the event type is not valid. 23 ErrEventTypeRegex = fmt.Errorf("event type must match %s", eventTypeRegexRaw) 24 errNumAttributes = fmt.Errorf("maximum of %d attributes exceeded", 25 customEventAttributeLimit) 26 ) 27 28 // CustomEvent is a custom event. 29 type CustomEvent struct { 30 eventType string 31 timestamp time.Time 32 truncatedParams map[string]interface{} 33 } 34 35 // WriteJSON prepares JSON in the format expected by the collector. 36 func (e *CustomEvent) WriteJSON(buf *bytes.Buffer) { 37 w := jsonFieldsWriter{buf: buf} 38 buf.WriteByte('[') 39 buf.WriteByte('{') 40 w.stringField("type", e.eventType) 41 w.floatField("timestamp", timeToFloatSeconds(e.timestamp)) 42 buf.WriteByte('}') 43 44 buf.WriteByte(',') 45 buf.WriteByte('{') 46 w = jsonFieldsWriter{buf: buf} 47 for key, val := range e.truncatedParams { 48 writeAttributeValueJSON(&w, key, val) 49 } 50 buf.WriteByte('}') 51 52 buf.WriteByte(',') 53 buf.WriteByte('{') 54 buf.WriteByte('}') 55 buf.WriteByte(']') 56 } 57 58 // MarshalJSON is used for testing. 59 func (e *CustomEvent) MarshalJSON() ([]byte, error) { 60 buf := bytes.NewBuffer(make([]byte, 0, 256)) 61 62 e.WriteJSON(buf) 63 64 return buf.Bytes(), nil 65 } 66 67 func eventTypeValidate(eventType string) error { 68 if len(eventType) > attributeKeyLengthLimit { 69 return errEventTypeLength 70 } 71 if !eventTypeRegex.MatchString(eventType) { 72 return ErrEventTypeRegex 73 } 74 return nil 75 } 76 77 // CreateCustomEvent creates a custom event. 78 func CreateCustomEvent(eventType string, params map[string]interface{}, now time.Time) (*CustomEvent, error) { 79 if err := eventTypeValidate(eventType); nil != err { 80 return nil, err 81 } 82 83 if len(params) > customEventAttributeLimit { 84 return nil, errNumAttributes 85 } 86 87 truncatedParams := make(map[string]interface{}) 88 for key, val := range params { 89 val, err := ValidateUserAttribute(key, val) 90 if nil != err { 91 return nil, err 92 } 93 truncatedParams[key] = val 94 } 95 96 return &CustomEvent{ 97 eventType: eventType, 98 timestamp: now, 99 truncatedParams: truncatedParams, 100 }, nil 101 } 102 103 // MergeIntoHarvest implements Harvestable. 104 func (e *CustomEvent) MergeIntoHarvest(h *Harvest) { 105 h.CustomEvents.Add(e) 106 }