Skip to content

Commit

Permalink
more fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
kruskall committed Sep 15, 2023
1 parent 351fdf1 commit 35f4d11
Show file tree
Hide file tree
Showing 16 changed files with 470 additions and 174 deletions.
12 changes: 6 additions & 6 deletions input/elasticapm/internal/modeldecoder/modeldecoderutil/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,18 +45,18 @@ func HTTPHeadersToStructPb(h http.Header) *structpb.Struct {
return nil
}

func HTTPHeadersToModelpb(h http.Header) []*modelpb.HTTPHeader {
func HTTPHeadersToModelpb(h http.Header, out []*modelpb.HTTPHeader) {
if len(h) == 0 {
return nil
return
}
headers := make([]*modelpb.HTTPHeader, 0, len(h))

i := 0
for k, v := range h {
pbheader := modelpb.HTTPHeaderFromVTPool()
pbheader := out[i]
pbheader.Key = k
pbheader.Value = v
headers = append(headers, pbheader)
i++
}
return headers
}

// NormalizeHTTPRequestBody recurses through v, replacing any instance of
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,20 +22,18 @@ import (
"google.golang.org/protobuf/types/known/structpb"
)

func ToKv(m map[string]any) []*modelpb.KeyValue {
func MapToKv(m map[string]any, out []*modelpb.KeyValue) {
nm := normalizeMap(m)
if len(nm) == 0 {
return nil
return
}

kv := []*modelpb.KeyValue{}
for k, v := range m {
i := 0
for k, v := range nm {
value, _ := structpb.NewValue(v)
pbkv := modelpb.KeyValueFromVTPool()
pbkv := out[i]
pbkv.Key = k
pbkv.Value = value
kv = append(kv, pbkv)
i++
}

return kv
}
49 changes: 25 additions & 24 deletions input/elasticapm/internal/modeldecoder/modeldecoderutil/labels.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,39 @@ func GlobalLabelsFrom(from map[string]any, to *modelpb.APMEvent) {
v.Global = true
to.NumericLabels[k] = v
}
if len(to.Labels) == 0 {
to.Labels = nil
}
if len(to.NumericLabels) == 0 {
to.NumericLabels = nil
}
}

// MergeLabels merges eventLabels into the APMEvent. This is used for
// combining event-specific labels onto (metadata) global labels.
//
// If eventLabels is non-nil, it is first cloned.
func MergeLabels(eventLabels map[string]any, to *modelpb.APMEvent) {
if to.NumericLabels == nil {
to.NumericLabels = make(modelpb.NumericLabels)
count := 0
numericCount := 0
for _, v := range eventLabels {
switch v.(type) {
case string, bool:
count++
case float64, json.Number:
numericCount++
}
}
if to.Labels == nil {
to.Labels = make(modelpb.Labels)

if count == 0 && numericCount == 0 {
return
}

if to.NumericLabels == nil && numericCount > 0 {
to.NumericLabels = make(modelpb.NumericLabels, numericCount)
}
if to.Labels == nil && count > 0 {
to.Labels = make(modelpb.Labels, count)
}
for k, v := range eventLabels {
switch v := v.(type) {
Expand All @@ -65,24 +86,4 @@ func MergeLabels(eventLabels map[string]any, to *modelpb.APMEvent) {
}
}
}
if len(to.NumericLabels) == 0 {
to.NumericLabels = nil
}
if len(to.Labels) == 0 {
to.Labels = nil
}
}

// NormalizeLabelValues transforms the values in labels, replacing any
// instance of json.Number with float64, and returning labels.
func NormalizeLabelValues(labels map[string]any) map[string]any {
for k, v := range labels {
switch v := v.(type) {
case json.Number:
if floatVal, err := v.Float64(); err == nil {
labels[k] = floatVal
}
}
}
return labels
}
14 changes: 14 additions & 0 deletions input/elasticapm/internal/modeldecoder/modeldecoderutil/slice.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package modeldecoderutil

func Reslice[S ~[]E, E any](s S, n int, fn func() E) S {
if diff := n - cap(s); diff > 0 {
extra := make([]E, diff, diff)
if fn != nil {
for i := range extra {
extra[i] = fn()
}
}
s = append([]E(s)[:cap(s)], extra...)
}
return s[:n]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package modeldecoderutil

import (
"testing"

"github.com/elastic/apm-data/model/modelpb"
"github.com/stretchr/testify/assert"
)

func TestReslice(t *testing.T) {
originalCap := 10
s := make([]*modelpb.APMEvent, originalCap, originalCap)
for i := range s {
s[i] = &modelpb.APMEvent{}
}

downsize := 4
s = Reslice(s, downsize, nil)
for _, e := range s {
assert.NotNil(t, e)
}
assert.Equal(t, downsize, len(s))
assert.Equal(t, originalCap, cap(s))

upsize := 20
s = Reslice(s, upsize, func() *modelpb.APMEvent { return &modelpb.APMEvent{} })
assert.Equal(t, upsize, len(s))
for _, e := range s {
t.Log(e)

Check failure on line 29 in input/elasticapm/internal/modeldecoder/modeldecoderutil/slice_test.go

View workflow job for this annotation

GitHub Actions / lint

should use make([]*modelpb.APMEvent, originalCap) instead (S1019)
assert.NotNil(t, e)
}
assert.Equal(t, upsize, cap(s))

}
46 changes: 30 additions & 16 deletions input/elasticapm/internal/modeldecoder/rumv3/decoder.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,12 @@ func DecodeNestedError(d decoder.Decoder, input *modeldecoder.Input, batch *mode
return modeldecoder.NewValidationErr(err)
}
event := input.Base.CloneVT()
for k := range event.Labels {
delete(event.Labels, k)
}
for k := range event.NumericLabels {
delete(event.NumericLabels, k)
}
mapToErrorModel(&root.Error, event)
*batch = append(*batch, event)
return nil
Expand Down Expand Up @@ -223,8 +229,9 @@ func mapToErrorModel(from *errorEvent, event *modelpb.APMEvent) {
event.Http.Request.Referrer = from.Context.Page.Referer.Val
}
}
if len(from.Context.Custom) > 0 {
out.Custom = modeldecoderutil.ToKv(from.Context.Custom)
if n := len(from.Context.Custom); n > 0 {
out.Custom = modeldecoderutil.Reslice(out.Custom, n, modelpb.KeyValueFromVTPool)
modeldecoderutil.MapToKv(from.Context.Custom, out.Custom)
}
}
if from.Culprit.IsSet() {
Expand Down Expand Up @@ -288,8 +295,9 @@ func mapToErrorModel(from *errorEvent, event *modelpb.APMEvent) {
}

func mapToExceptionModel(from errorException, out *modelpb.Exception) {
if len(from.Attributes) > 0 {
out.Attributes = modeldecoderutil.ToKv(from.Attributes)
if n := len(from.Attributes); n > 0 {
out.Attributes = modeldecoderutil.Reslice(out.Attributes, n, modelpb.KeyValueFromVTPool)
modeldecoderutil.MapToKv(from.Attributes, out.Attributes)
}
if from.Code.IsSet() {
out.Code = modeldecoderutil.ExceptionCodeString(from.Code.Val)
Expand Down Expand Up @@ -394,7 +402,7 @@ func mapToMetadataModel(m *metadata, out *modelpb.APMEvent) {
out.User = modelpb.UserFromVTPool()
}
if m.User.Domain.IsSet() {
out.User.Domain = fmt.Sprint(m.User.Domain.Val)
out.User.Domain = m.User.Domain.Val
}
if m.User.ID.IsSet() {
out.User.Id = fmt.Sprint(m.User.ID.Val)
Expand Down Expand Up @@ -456,7 +464,8 @@ func mapToTransactionMetricsetModel(from *transactionMetricset, event *modelpb.A

func mapToResponseModel(from contextResponse, out *modelpb.HTTPResponse) {
if from.Headers.IsSet() {
out.Headers = modeldecoderutil.HTTPHeadersToModelpb(from.Headers.Val)
out.Headers = modeldecoderutil.Reslice(out.Headers, len(from.Headers.Val), modelpb.HTTPHeaderFromVTPool)
modeldecoderutil.HTTPHeadersToModelpb(from.Headers.Val, out.Headers)
}
if from.StatusCode.IsSet() {
out.StatusCode = uint32(from.StatusCode.Val)
Expand All @@ -479,11 +488,13 @@ func mapToRequestModel(from contextRequest, out *modelpb.HTTPRequest) {
if from.Method.IsSet() {
out.Method = from.Method.Val
}
if len(from.Env) > 0 {
out.Env = modeldecoderutil.ToKv(from.Env)
if n := len(from.Env); n > 0 {
out.Env = modeldecoderutil.Reslice(out.Env, n, modelpb.KeyValueFromVTPool)
modeldecoderutil.MapToKv(from.Env, out.Env)
}
if from.Headers.IsSet() {
out.Headers = modeldecoderutil.HTTPHeadersToModelpb(from.Headers.Val)
out.Headers = modeldecoderutil.Reslice(out.Headers, len(from.Headers.Val), modelpb.HTTPHeaderFromVTPool)
modeldecoderutil.HTTPHeadersToModelpb(from.Headers.Val, out.Headers)
}
}

Expand Down Expand Up @@ -695,16 +706,18 @@ func mapToSpanModel(from *span, event *modelpb.APMEvent) {

func mapToStracktraceModel(from []stacktraceFrame, out []*modelpb.StacktraceFrame) {
for idx, eventFrame := range from {
fr := modelpb.StacktraceFrame{}
fr := modelpb.StacktraceFrameFromVTPool()
if eventFrame.AbsPath.IsSet() {
fr.AbsPath = eventFrame.AbsPath.Val
}
if eventFrame.Classname.IsSet() {
fr.Classname = eventFrame.Classname.Val
}
if eventFrame.ColumnNumber.IsSet() {
val := uint32(eventFrame.ColumnNumber.Val)
fr.Colno = &val
if fr.Colno == nil {
fr.Colno = new(uint32)
}
*fr.Colno = uint32(eventFrame.ColumnNumber.Val)
}
if eventFrame.ContextLine.IsSet() {
fr.ContextLine = eventFrame.ContextLine.Val
Expand All @@ -730,7 +743,7 @@ func mapToStracktraceModel(from []stacktraceFrame, out []*modelpb.StacktraceFram
fr.PreContext = make([]string, len(eventFrame.PreContext))
copy(fr.PreContext, eventFrame.PreContext)
}
out[idx] = &fr
out[idx] = fr
}
}

Expand Down Expand Up @@ -762,8 +775,9 @@ func mapToTransactionModel(from *transaction, event *modelpb.APMEvent) {

// map transaction specific data
if from.Context.IsSet() {
if len(from.Context.Custom) > 0 {
out.Custom = modeldecoderutil.ToKv(from.Context.Custom)
if n := len(from.Context.Custom); n > 0 {
out.Custom = modeldecoderutil.Reslice(out.Custom, n, modelpb.KeyValueFromVTPool)
modeldecoderutil.MapToKv(from.Context.Custom, out.Custom)
}
if len(from.Context.Tags) > 0 {
modeldecoderutil.MergeLabels(from.Context.Tags, event)
Expand Down Expand Up @@ -926,7 +940,7 @@ func overwriteUserInMetadataModel(from user, out *modelpb.APMEvent) {
}
out.User = modelpb.UserFromVTPool()
if from.Domain.IsSet() {
out.User.Domain = fmt.Sprint(from.Domain.Val)
out.User.Domain = from.Domain.Val
}
if from.ID.IsSet() {
out.User.Id = fmt.Sprint(from.ID.Val)
Expand Down
4 changes: 2 additions & 2 deletions input/elasticapm/internal/modeldecoder/rumv3/error_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -104,10 +104,10 @@ func TestDecodeMapToErrorModel(t *testing.T) {
// do not overwrite client.ip if already set in metadata
ip := modeldecodertest.DefaultValues().IP
assert.Equal(t, ip, out.Client.Ip)
assert.Equal(t, modelpb.Labels{
assert.Empty(t, cmp.Diff(modelpb.Labels{
"init0": {Global: true, Value: "init"}, "init1": {Global: true, Value: "init"}, "init2": {Global: true, Value: "init"},
"overwritten0": {Value: "overwritten"}, "overwritten1": {Value: "overwritten"},
}, modelpb.Labels(out.Labels))
}, modelpb.Labels(out.Labels), protocmp.Transform()))
// service and user values should be set
modeldecodertest.AssertStructValues(t, &out.Service, metadataExceptions("node", "Agent.EphemeralID"), otherVal)
modeldecodertest.AssertStructValues(t, &out.User, metadataExceptions(), otherVal)
Expand Down
6 changes: 4 additions & 2 deletions input/elasticapm/internal/modeldecoder/rumv3/metadata_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@ import (
"strings"
"testing"

"github.com/google/go-cmp/cmp"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/testing/protocmp"

"github.com/elastic/apm-data/input/elasticapm/internal/decoder"
"github.com/elastic/apm-data/input/elasticapm/internal/modeldecoder/modeldecodertest"
Expand Down Expand Up @@ -222,14 +224,14 @@ func TestDecodeMetadataMappingToModel(t *testing.T) {
otherVal := modeldecodertest.NonDefaultValues()
modeldecodertest.SetStructValues(&input, otherVal)
mapToMetadataModel(&input, out)
assert.Equal(t, expected(otherVal.Str, otherVal.IP, otherVal.N), out)
assert.Empty(t, cmp.Diff(expected(otherVal.Str, otherVal.IP, otherVal.N), out, protocmp.Transform()))

// map an empty modeldecoder metadata to the model
// and assert values are unchanged
input.Reset()
modeldecodertest.SetZeroStructValues(&input)
mapToMetadataModel(&input, out)
assert.Equal(t, expected(otherVal.Str, otherVal.IP, otherVal.N), out)
assert.Empty(t, cmp.Diff(expected(otherVal.Str, otherVal.IP, otherVal.N), out, protocmp.Transform()))
})

t.Run("reused-memory", func(t *testing.T) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -164,10 +164,10 @@ func TestDecodeMapToTransactionModel(t *testing.T) {
assert.Equal(t, "d, e", out.UserAgent.Original)
// do not overwrite client.ip if already set in metadata
assert.Equal(t, localhostIP, out.Client.Ip)
assert.Equal(t, modelpb.Labels{
assert.Empty(t, cmp.Diff(modelpb.Labels{
"init0": {Global: true, Value: "init"}, "init1": {Global: true, Value: "init"}, "init2": {Global: true, Value: "init"},
"overwritten0": {Value: "overwritten"}, "overwritten1": {Value: "overwritten"},
}, modelpb.Labels(out.Labels))
}, modelpb.Labels(out.Labels), protocmp.Transform()))
// service values should be set
modeldecodertest.AssertStructValues(t, &out.Service, metadataExceptions("node", "Agent.EphemeralID"), otherVal)
// user values should be set
Expand Down
Loading

0 comments on commit 35f4d11

Please sign in to comment.