Skip to content

Commit

Permalink
Merge pull request newrelic#52 from will/event-intrinsics
Browse files Browse the repository at this point in the history
JSON object fields writer
  • Loading branch information
will@newrelic.com committed Aug 10, 2016
2 parents f317714 + 2caf591 commit 5eac3bb
Show file tree
Hide file tree
Showing 6 changed files with 143 additions and 156 deletions.
83 changes: 29 additions & 54 deletions internal/attributes.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ import (
"sort"
"strconv"
"strings"

"github.com/newrelic/go-agent/internal/jsonx"
)

// New agent attributes must be added in the following places:
Expand Down Expand Up @@ -261,42 +259,26 @@ func calculateAgentAttributeDests(c *AttributeConfig) agentAttributeDests {
}

type agentAttributeWriter struct {
needsComma bool
buf *bytes.Buffer
d destinationSet
}

func (w *agentAttributeWriter) writePrefix(name string, d destinationSet) bool {
if 0 != w.d&d {
if w.needsComma {
w.buf.WriteByte(',')
} else {
w.needsComma = true
}
jsonx.AppendString(w.buf, name)
w.buf.WriteByte(':')
return true
}
return false
jsonFieldsWriter
d destinationSet
}

func (w *agentAttributeWriter) writeString(name string, val string, d destinationSet) {
if "" != val && w.writePrefix(name, d) {
jsonx.AppendString(w.buf, truncateStringValueIfLong(val))
if "" != val && 0 != w.d&d {
w.stringField(name, truncateStringValueIfLong(val))
}
}

func (w *agentAttributeWriter) writeInt(name string, val int, d destinationSet) {
if val >= 0 && w.writePrefix(name, d) {
jsonx.AppendInt(w.buf, int64(val))
if val >= 0 && 0 != w.d&d {
w.intField(name, int64(val))
}
}

func writeAgentAttributes(buf *bytes.Buffer, d destinationSet, values agentAttributes, dests agentAttributeDests) {
w := &agentAttributeWriter{
needsComma: false,
buf: buf,
d: d,
jsonFieldsWriter: jsonFieldsWriter{buf: buf},
d: d,
}
buf.WriteByte('{')
w.writeString(hostDisplayName, values.HostDisplayName, dests.HostDisplayName)
Expand Down Expand Up @@ -411,46 +393,46 @@ func AddUserAttribute(a *Attributes, key string, val interface{}, d destinationS
return nil
}

func writeAttributeValueJSON(buf *bytes.Buffer, val interface{}) {
func writeAttributeValueJSON(w *jsonFieldsWriter, key string, val interface{}) {
switch v := val.(type) {
case nil:
buf.WriteString(`null`)
w.rawField(key, `null`)
case string:
jsonx.AppendString(buf, v)
w.stringField(key, v)
case bool:
if v {
buf.WriteString(`true`)
w.rawField(key, `true`)
} else {
buf.WriteString(`false`)
w.rawField(key, `false`)
}
case uint8:
jsonx.AppendUint(buf, uint64(v))
w.intField(key, int64(v))
case uint16:
jsonx.AppendUint(buf, uint64(v))
w.intField(key, int64(v))
case uint32:
jsonx.AppendUint(buf, uint64(v))
w.intField(key, int64(v))
case uint64:
jsonx.AppendUint(buf, v)
w.intField(key, int64(v))
case uint:
jsonx.AppendUint(buf, uint64(v))
w.intField(key, int64(v))
case uintptr:
jsonx.AppendUint(buf, uint64(v))
w.intField(key, int64(v))
case int8:
jsonx.AppendInt(buf, int64(v))
w.intField(key, int64(v))
case int16:
jsonx.AppendInt(buf, int64(v))
w.intField(key, int64(v))
case int32:
jsonx.AppendInt(buf, int64(v))
w.intField(key, int64(v))
case int64:
jsonx.AppendInt(buf, v)
w.intField(key, v)
case int:
jsonx.AppendInt(buf, int64(v))
w.intField(key, int64(v))
case float32:
jsonx.AppendFloat(buf, float64(v))
w.floatField(key, float64(v))
case float64:
jsonx.AppendFloat(buf, v)
w.floatField(key, v)
default:
jsonx.AppendString(buf, fmt.Sprintf("%T", v))
w.stringField(key, fmt.Sprintf("%T", v))
}
}

Expand All @@ -465,17 +447,10 @@ func agentAttributesJSON(a *Attributes, buf *bytes.Buffer, d destinationSet) {
func userAttributesJSON(a *Attributes, buf *bytes.Buffer, d destinationSet) {
buf.WriteByte('{')
if nil != a {
first := true
w := jsonFieldsWriter{buf: buf}
for name, atr := range a.user {
if 0 != atr.dests&d {
if first {
first = false
} else {
buf.WriteByte(',')
}
jsonx.AppendString(buf, name)
buf.WriteByte(':')
writeAttributeValueJSON(buf, atr.value)
writeAttributeValueJSON(&w, name, atr.value)
}
}
}
Expand Down
75 changes: 39 additions & 36 deletions internal/attributes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,44 +149,47 @@ func TestCrossAgentAttributes(t *testing.T) {

func TestWriteAttributeValueJSON(t *testing.T) {
buf := &bytes.Buffer{}
w := jsonFieldsWriter{buf: buf}

buf.WriteByte('[')
writeAttributeValueJSON(buf, nil)
buf.WriteByte(',')
writeAttributeValueJSON(buf, `escape\me!`)
buf.WriteByte(',')
writeAttributeValueJSON(buf, true)
buf.WriteByte(',')
writeAttributeValueJSON(buf, false)
buf.WriteByte(',')
writeAttributeValueJSON(buf, uint8(1))
buf.WriteByte(',')
writeAttributeValueJSON(buf, uint16(2))
buf.WriteByte(',')
writeAttributeValueJSON(buf, uint32(3))
buf.WriteByte(',')
writeAttributeValueJSON(buf, uint64(4))
buf.WriteByte(',')
writeAttributeValueJSON(buf, uint(5))
buf.WriteByte(',')
writeAttributeValueJSON(buf, uintptr(6))
buf.WriteByte(',')
writeAttributeValueJSON(buf, int8(-1))
buf.WriteByte(',')
writeAttributeValueJSON(buf, int16(-2))
buf.WriteByte(',')
writeAttributeValueJSON(buf, int32(-3))
buf.WriteByte(',')
writeAttributeValueJSON(buf, int64(-4))
buf.WriteByte(',')
writeAttributeValueJSON(buf, int(-5))
buf.WriteByte(',')
writeAttributeValueJSON(buf, float32(1.5))
buf.WriteByte(',')
writeAttributeValueJSON(buf, float64(4.56))
buf.WriteByte(']')
buf.WriteByte('{')
writeAttributeValueJSON(&w, "a", nil)
writeAttributeValueJSON(&w, "a", `escape\me!`)
writeAttributeValueJSON(&w, "a", true)
writeAttributeValueJSON(&w, "a", false)
writeAttributeValueJSON(&w, "a", uint8(1))
writeAttributeValueJSON(&w, "a", uint16(2))
writeAttributeValueJSON(&w, "a", uint32(3))
writeAttributeValueJSON(&w, "a", uint64(4))
writeAttributeValueJSON(&w, "a", uint(5))
writeAttributeValueJSON(&w, "a", uintptr(6))
writeAttributeValueJSON(&w, "a", int8(-1))
writeAttributeValueJSON(&w, "a", int16(-2))
writeAttributeValueJSON(&w, "a", int32(-3))
writeAttributeValueJSON(&w, "a", int64(-4))
writeAttributeValueJSON(&w, "a", int(-5))
writeAttributeValueJSON(&w, "a", float32(1.5))
writeAttributeValueJSON(&w, "a", float64(4.56))
buf.WriteByte('}')

expect := `[null,"escape\\me!",true,false,1,2,3,4,5,6,-1,-2,-3,-4,-5,1.5,4.56]`
expect := CompactJSONString(`{
"a":null,
"a":"escape\\me!",
"a":true,
"a":false,
"a":1,
"a":2,
"a":3,
"a":4,
"a":5,
"a":6,
"a":-1,
"a":-2,
"a":-3,
"a":-4,
"a":-5,
"a":1.5,
"a":4.56
}`)
js := string(buf.Bytes())
if js != expect {
t.Error(js, expect)
Expand Down
21 changes: 5 additions & 16 deletions internal/custom_event.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ import (
"fmt"
"regexp"
"time"

"github.com/newrelic/go-agent/internal/jsonx"
)

// https://newrelic.atlassian.net/wiki/display/eng/Custom+Events+in+New+Relic+Agents
Expand All @@ -33,27 +31,18 @@ type CustomEvent struct {

// 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('{')
buf.WriteString(`"type":`)
jsonx.AppendString(buf, e.eventType)
buf.WriteByte(',')
buf.WriteString(`"timestamp":`)
jsonx.AppendFloat(buf, timeToFloatSeconds(e.timestamp))
w.stringField("type", e.eventType)
w.floatField("timestamp", timeToFloatSeconds(e.timestamp))
buf.WriteByte('}')

buf.WriteByte(',')
buf.WriteByte('{')
first := true
w = jsonFieldsWriter{buf: buf}
for key, val := range e.truncatedParams {
if first {
first = false
} else {
buf.WriteByte(',')
}
jsonx.AppendString(buf, key)
buf.WriteByte(':')
writeAttributeValueJSON(buf, val)
writeAttributeValueJSON(&w, key, val)
}
buf.WriteByte('}')

Expand Down
44 changes: 14 additions & 30 deletions internal/error_events.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import (
"bytes"
"math/rand"
"time"

"github.com/newrelic/go-agent/internal/jsonx"
)

// ErrorEvent is an error event.
Expand All @@ -32,42 +30,28 @@ func (e *ErrorEvent) MarshalJSON() ([]byte, error) {
// WriteJSON prepares JSON in the format expected by the collector.
// https://source.datanerd.us/agents/agent-specs/blob/master/Error-Events.md
func (e *ErrorEvent) WriteJSON(buf *bytes.Buffer) {
buf.WriteString(`[{"type":"TransactionError","error.class":`)
jsonx.AppendString(buf, e.Klass)

buf.WriteString(`,"error.message":`)
jsonx.AppendString(buf, e.Msg)

buf.WriteString(`,"timestamp":`)
jsonx.AppendFloat(buf, timeToFloatSeconds(e.When))

buf.WriteString(`,"transactionName":`)
jsonx.AppendString(buf, e.TxnName)

buf.WriteString(`,"duration":`)
jsonx.AppendFloat(buf, e.Duration.Seconds())

w := jsonFieldsWriter{buf: buf}
buf.WriteByte('[')
buf.WriteByte('{')
w.stringField("type", "TransactionError")
w.stringField("error.class", e.Klass)
w.stringField("error.message", e.Msg)
w.floatField("timestamp", timeToFloatSeconds(e.When))
w.stringField("transactionName", e.TxnName)
w.floatField("duration", e.Duration.Seconds())
if e.Queuing > 0 {
buf.WriteString(`,"queueDuration":`)
jsonx.AppendFloat(buf, e.Queuing.Seconds())
w.floatField("queueDuration", e.Queuing.Seconds())
}

if e.externalCallCount > 0 {
buf.WriteString(`,"externalCallCount":`)
jsonx.AppendInt(buf, int64(e.externalCallCount))
buf.WriteString(`,"externalDuration":`)
jsonx.AppendFloat(buf, e.externalDuration.Seconds())
w.intField("externalCallCount", int64(e.externalCallCount))
w.floatField("externalDuration", e.externalDuration.Seconds())
}

if e.datastoreCallCount > 0 {
// Note that "database" is used for the keys here instead of
// "datastore" for historical reasons.
buf.WriteString(`,"databaseCallCount":`)
jsonx.AppendInt(buf, int64(e.datastoreCallCount))
buf.WriteString(`,"databaseDuration":`)
jsonx.AppendFloat(buf, e.datastoreDuration.Seconds())
w.intField("databaseCallCount", int64(e.datastoreCallCount))
w.floatField("databaseDuration", e.datastoreDuration.Seconds())
}

buf.WriteByte('}')
buf.WriteByte(',')
userAttributesJSON(e.Attrs, buf, destError)
Expand Down
43 changes: 43 additions & 0 deletions internal/json_object_writer.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package internal

import (
"bytes"

"github.com/newrelic/go-agent/internal/jsonx"
)

type jsonFieldsWriter struct {
buf *bytes.Buffer
needsComma bool
}

func (w *jsonFieldsWriter) addKey(key string) {
if w.needsComma {
w.buf.WriteByte(',')
} else {
w.needsComma = true
}
// defensively assume that the key needs escaping:
jsonx.AppendString(w.buf, key)
w.buf.WriteByte(':')
}

func (w *jsonFieldsWriter) stringField(key string, val string) {
w.addKey(key)
jsonx.AppendString(w.buf, val)
}

func (w *jsonFieldsWriter) intField(key string, val int64) {
w.addKey(key)
jsonx.AppendInt(w.buf, val)
}

func (w *jsonFieldsWriter) floatField(key string, val float64) {
w.addKey(key)
jsonx.AppendFloat(w.buf, val)
}

func (w *jsonFieldsWriter) rawField(key string, val JSONString) {
w.addKey(key)
w.buf.WriteString(string(val))
}
Loading

0 comments on commit 5eac3bb

Please sign in to comment.