Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 11 additions & 8 deletions v3/integrations/logcontext-v2/nrslog/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,7 @@ func TestWithAttributes(t *testing.T) {

txn := app.StartTransaction("hi")
txnLog := WithTransaction(txn, log)
txnLog.Info(message)
txnLog.Info(message, slog.Duration("duration", 3*time.Second))
data := txn.GetLinkingMetadata()
txn.End()

Expand All @@ -309,11 +309,13 @@ func TestWithAttributes(t *testing.T) {
log = log.With(additionalAttrs)
log.Info(message)

expectInt := int64(1)

app.ExpectLogEvents(t, []internal.WantLog{
{
Attributes: map[string]interface{}{
"string key": "val",
"int key": 1,
"int key": expectInt,
},
Severity: slog.LevelInfo.String(),
Message: message,
Expand All @@ -322,7 +324,8 @@ func TestWithAttributes(t *testing.T) {
{
Attributes: map[string]interface{}{
"string key": "val",
"int key": 1,
"int key": expectInt,
"duration": time.Duration(3 * time.Second),
},
Severity: slog.LevelInfo.String(),
Message: message,
Expand All @@ -333,7 +336,7 @@ func TestWithAttributes(t *testing.T) {
{
Attributes: map[string]interface{}{
"string key": "val",
"int key": 1,
"int key": expectInt,
"group1.additional": "attr",
},
Severity: slog.LevelInfo.String(),
Expand All @@ -343,7 +346,7 @@ func TestWithAttributes(t *testing.T) {
{
Attributes: map[string]interface{}{
"string key": "val",
"int key": 1,
"int key": expectInt,
"group1.group2.additional": "attr",
},
Severity: slog.LevelInfo.String(),
Expand All @@ -353,7 +356,7 @@ func TestWithAttributes(t *testing.T) {
{
Attributes: map[string]interface{}{
"string key": "val",
"int key": 1,
"int key": expectInt,
"group1.group2.additional": "attr",
},
Severity: slog.LevelInfo.String(),
Expand Down Expand Up @@ -417,7 +420,7 @@ func TestWithAttributesFromContext(t *testing.T) {
Timestamp: internal.MatchAnyUnixMilli,
Attributes: map[string]interface{}{
"foo": "bar",
"answer": 42,
"answer": int64(42),
},
TraceID: metadata.TraceID,
SpanID: metadata.SpanID,
Expand All @@ -428,7 +431,7 @@ func TestWithAttributesFromContext(t *testing.T) {
Timestamp: internal.MatchAnyUnixMilli,
Attributes: map[string]interface{}{
"group1.foo": "bar",
"group1.answer": 42,
"group1.answer": int64(42),
},
},
})
Expand Down
11 changes: 11 additions & 0 deletions v3/newrelic/attributes_from_internal.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"sort"
"strconv"
"strings"
"time"
)

const (
Expand Down Expand Up @@ -507,6 +508,16 @@ func writeAttributeValueJSON(w *jsonFieldsWriter, key string, val interface{}) {
w.floatField(key, float64(v))
case float64:
w.floatField(key, v)
case time.Time:
writeAttributeValueJSON(w, key, v.String())
case time.Duration:
writeAttributeValueJSON(w, key, v.String())
case time.Weekday:
writeAttributeValueJSON(w, key, v.String())
case *time.Location:
writeAttributeValueJSON(w, key, v.String())
case time.Month:
writeAttributeValueJSON(w, key, v.String())
default:
// attempt to construct a JSON string
kind := reflect.ValueOf(v).Kind()
Expand Down
13 changes: 12 additions & 1 deletion v3/newrelic/attributes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"strconv"
"strings"
"testing"
"time"

"github.com/newrelic/go-agent/v3/internal/crossagent"
)
Expand Down Expand Up @@ -170,6 +171,11 @@ func TestWriteAttributeValueJSON(t *testing.T) {
writeAttributeValueJSON(&w, "a", int(-5))
writeAttributeValueJSON(&w, "a", float32(1.5))
writeAttributeValueJSON(&w, "a", float64(4.56))
writeAttributeValueJSON(&w, "duration", time.Duration(7))
writeAttributeValueJSON(&w, "time", time.Time{}.AddDate(2000, 1, 1))
writeAttributeValueJSON(&w, "weekday", time.Wednesday)
writeAttributeValueJSON(&w, "month", time.April)
writeAttributeValueJSON(&w, "location", time.FixedZone("LOC", 0))
buf.WriteByte('}')

expect := compactJSONString(`{
Expand All @@ -188,7 +194,12 @@ func TestWriteAttributeValueJSON(t *testing.T) {
"a":-4,
"a":-5,
"a":1.5,
"a":4.56
"a":4.56,
"duration":"7ns",
"time":"2001-02-02 00:00:00 +0000 UTC",
"weekday":"Wednesday",
"month":"April",
"location":"LOC"
}`)
js := buf.String()
if js != expect {
Expand Down
77 changes: 19 additions & 58 deletions v3/newrelic/expect_implementation.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"bytes"
"encoding/json"
"fmt"
"reflect"
"time"

"github.com/newrelic/go-agent/v3/internal"
Expand Down Expand Up @@ -251,73 +252,33 @@ func expectLogEvent(v internal.Validator, actual logEvent, want internal.WantLog
}

if actual.attributes != nil && want.Attributes != nil {
for k, val := range want.Attributes {
actualVal, actualOk := actual.attributes[k]
if !actualOk {
v.Error(fmt.Sprintf("expected log attribute for key %v is missing", k))
return
if len(actual.attributes) != len(want.Attributes) {
skippedAttributes := []string{}
for k := range actual.attributes {
if _, ok := want.Attributes[k]; !ok {
skippedAttributes = append(skippedAttributes, fmt.Sprintf("an expected attribute is missing: {\"%s\":%v}", k, actual.attributes[k]))
}
}

// Check if both values are maps, and if so, compare them recursively
if expectedMap, ok := val.(map[string]interface{}); ok {
if actualMap, ok := actualVal.(map[string]interface{}); ok {
if !expectLogEventAttributesMaps(expectedMap, actualMap) {
v.Error(fmt.Sprintf("unexpected log attribute for key %v: got %v, want %v", k, actualMap, expectedMap))
return
}
} else {
v.Error(fmt.Sprintf("actual value for key %v is not a map", k))
return
for k := range want.Attributes {
if _, ok := actual.attributes[k]; !ok {
skippedAttributes = append(skippedAttributes, fmt.Sprintf("unexpected attribute: {\"%s\":%v}", k, want.Attributes[k]))
}
}
}
}

}

// Helper function that compares two maps for equality. This is used to compare the attribute fields of log events expected vs received
func expectLogEventAttributesMaps(a, b map[string]interface{}) bool {
if len(a) != len(b) {
return false
}
for k, v := range a {
if bv, ok := b[k]; !ok {
return false
v.Error(fmt.Sprintf("unexpected number of log attributes: got %d, want %d; %s", len(actual.attributes), len(want.Attributes), skippedAttributes))
return
} else {
switch v := v.(type) {
case float64:
if bv, ok := bv.(float64); !ok || v != bv {
return false
}

case int:
if bv, ok := bv.(int); !ok || v != bv {
return false
}
case time.Duration:
if bv, ok := bv.(time.Duration); ok {
return v == bv
}
case string:
if bv, ok := bv.(string); !ok || v != bv {
return false
}
case int64:
if bv, ok := bv.(int64); !ok || v != bv {
return false
}
// if the type of the field is a map, recursively compare the maps
case map[string]interface{}:
if bv, ok := bv.(map[string]interface{}); !ok || !expectLogEventAttributesMaps(v, bv) {
return false
for k, wantVal := range want.Attributes {
actualVal := actual.attributes[k]
ok := reflect.DeepEqual(wantVal, actualVal)
if !ok {
v.Error(fmt.Sprintf("unexpected log attribute for key \"%s\": got value: %+v, type: %T; want value: %+v, type: %T", k, actualVal, actualVal, wantVal, wantVal))
return
}
default:
return false
}
}
}
return true
}

func expectEvent(v internal.Validator, e json.Marshaler, expect internal.WantEvent) {
js, err := e.MarshalJSON()
if nil != err {
Expand Down
Loading