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