Skip to content

Commit

Permalink
Added support for elastic.profiler_stack_trace_ids OTel attribute (#202)
Browse files Browse the repository at this point in the history
Maps the Otel attribute elastic.profiler_stack_trace_ids to the new ES field transaction.profiler_stack_trace_ids, which will use counted_keyword as type.
  • Loading branch information
JonasKunz authored Feb 7, 2024
1 parent 8b51b7a commit 668fa48
Show file tree
Hide file tree
Showing 11 changed files with 230 additions and 92 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ func TestDecodeMapToTransactionModel(t *testing.T) {
exceptions := func(key string) bool {
for _, s := range []string{
// values not set for RUM v3
"Kind", "representative_count", "message", "dropped_spans_stats",
"Kind", "representative_count", "message", "dropped_spans_stats", "profiler_stack_trace_ids",
// Not set for transaction events:
"AggregatedDuration",
"AggregatedDuration.Count",
Expand Down
15 changes: 15 additions & 0 deletions input/elasticapm/internal/modeldecoder/v2/transaction_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -268,6 +268,8 @@ func TestDecodeMapToTransactionModel(t *testing.T) {
"representative_count",
// Kind is tested further down
"Kind",
// profiler_stack_trace_ids are supplied as OTel-attributes
"profiler_stack_trace_ids",

// Not set for transaction events, tested in metricset decoding:
"AggregatedDuration",
Expand Down Expand Up @@ -636,6 +638,19 @@ func TestDecodeMapToTransactionModel(t *testing.T) {
mapToTransactionModel(&input, &event)
assert.Equal(t, "CLIENT", event.Span.Kind)
})

t.Run("elastic-profiling-ids", func(t *testing.T) {
var input transaction
var event modelpb.APMEvent
modeldecodertest.SetStructValues(&input, modeldecodertest.DefaultValues())
attrs := map[string]interface{}{
"elastic.profiler_stack_trace_ids": []interface{}{"id1", "id2"},
}
input.OTel.Attributes = attrs

mapToTransactionModel(&input, &event)
assert.Equal(t, []string{"id1", "id2"}, event.Transaction.ProfilerStackTraceIds)
})
})
t.Run("labels", func(t *testing.T) {
var input transaction
Expand Down
15 changes: 14 additions & 1 deletion input/otlp/traces.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import (
"math"
"net"
"net/url"
"slices"
"strconv"
"strings"
"time"
Expand Down Expand Up @@ -275,7 +276,19 @@ func TranslateTransaction(
k := replaceDots(kDots)
switch v.Type() {
case pcommon.ValueTypeSlice:
setLabel(k, event, ifaceAttributeValue(v))
switch kDots {
case "elastic.profiler_stack_trace_ids":
var vSlice = v.Slice()
event.Transaction.ProfilerStackTraceIds = slices.Grow(event.Transaction.ProfilerStackTraceIds, vSlice.Len())
for i := 0; i < vSlice.Len(); i++ {
var idVal = vSlice.At(i)
if idVal.Type() == pcommon.ValueTypeStr {
event.Transaction.ProfilerStackTraceIds = append(event.Transaction.ProfilerStackTraceIds, idVal.Str())
}
}
default:
setLabel(k, event, ifaceAttributeValue(v))
}
case pcommon.ValueTypeBool:
setLabel(k, event, ifaceAttributeValue(v))
case pcommon.ValueTypeDouble:
Expand Down
20 changes: 20 additions & 0 deletions input/otlp/traces_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -949,6 +949,26 @@ func TestArrayLabels(t *testing.T) {
}, modelpb.NumericLabels(spanEvent.NumericLabels))
}

func TestProfilerStackTraceIds(t *testing.T) {
validIds := []interface{}{"myId1", "myId2"}
badValueTypes := []interface{}{42, 68, "valid"}

tx1 := transformTransactionWithAttributes(t, map[string]interface{}{
"elastic.profiler_stack_trace_ids": validIds,
})
assert.Equal(t, []string{"myId1", "myId2"}, tx1.Transaction.ProfilerStackTraceIds)

tx2 := transformTransactionWithAttributes(t, map[string]interface{}{
"elastic.profiler_stack_trace_ids": badValueTypes,
})
assert.Equal(t, []string{"valid"}, tx2.Transaction.ProfilerStackTraceIds)

tx3 := transformTransactionWithAttributes(t, map[string]interface{}{
"elastic.profiler_stack_trace_ids": "bad type",
})
assert.Equal(t, []string(nil), tx3.Transaction.ProfilerStackTraceIds)
}

func TestConsumeTracesExportTimestamp(t *testing.T) {
traces, otelSpans := newTracesSpans()

Expand Down
17 changes: 17 additions & 0 deletions model/modeljson/internal/marshal_fastjson.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 16 additions & 15 deletions model/modeljson/internal/transaction.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,22 @@
package modeljson

type Transaction struct {
SpanCount SpanCount `json:"span_count,omitempty"`
UserExperience *UserExperience `json:"experience,omitempty"`
Custom KeyValueSlice `json:"custom,omitempty"`
Marks map[string]map[string]float64 `json:"marks,omitempty"`
Message *Message `json:"message,omitempty"`
Type string `json:"type,omitempty"`
Name string `json:"name,omitempty"`
Result string `json:"result,omitempty"`
ID string `json:"id,omitempty"`
DurationHistogram Histogram `json:"duration.histogram,omitempty"`
DroppedSpansStats []DroppedSpanStats `json:"dropped_spans_stats,omitempty"`
DurationSummary SummaryMetric `json:"duration.summary,omitempty"`
RepresentativeCount float64 `json:"representative_count,omitempty"`
Sampled bool `json:"sampled,omitempty"`
Root bool `json:"root,omitempty"`
SpanCount SpanCount `json:"span_count,omitempty"`
UserExperience *UserExperience `json:"experience,omitempty"`
Custom KeyValueSlice `json:"custom,omitempty"`
Marks map[string]map[string]float64 `json:"marks,omitempty"`
Message *Message `json:"message,omitempty"`
Type string `json:"type,omitempty"`
Name string `json:"name,omitempty"`
Result string `json:"result,omitempty"`
ID string `json:"id,omitempty"`
DurationHistogram Histogram `json:"duration.histogram,omitempty"`
DroppedSpansStats []DroppedSpanStats `json:"dropped_spans_stats,omitempty"`
ProfilerStackTraceIds []string `json:"profiler_stack_trace_ids,omitempty"`
DurationSummary SummaryMetric `json:"duration.summary,omitempty"`
RepresentativeCount float64 `json:"representative_count,omitempty"`
Sampled bool `json:"sampled,omitempty"`
Root bool `json:"root,omitempty"`
}

type SpanCount struct {
Expand Down
15 changes: 8 additions & 7 deletions model/modeljson/transaction.pb.json.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,14 @@ import (

func TransactionModelJSON(e *modelpb.Transaction, out *modeljson.Transaction, metricset bool) {
*out = modeljson.Transaction{
ID: e.Id,
Type: e.Type,
Name: e.Name,
Result: e.Result,
Sampled: e.Sampled,
Root: e.Root,
RepresentativeCount: e.RepresentativeCount,
ID: e.Id,
Type: e.Type,
Name: e.Name,
Result: e.Result,
Sampled: e.Sampled,
Root: e.Root,
RepresentativeCount: e.RepresentativeCount,
ProfilerStackTraceIds: e.ProfilerStackTraceIds,
}

if e.Custom != nil {
Expand Down
21 changes: 12 additions & 9 deletions model/modeljson/transaction.pb.json_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,9 +106,10 @@ func TestTransactionToModelJSON(t *testing.T) {
Count: 6,
Sum: 7,
},
RepresentativeCount: 8,
Sampled: true,
Root: true,
RepresentativeCount: 8,
Sampled: true,
Root: true,
ProfilerStackTraceIds: []string{"foo", "foo", "bar"},
},
expectedNoMetricset: &modeljson.Transaction{
SpanCount: modeljson.SpanCount{
Expand All @@ -134,9 +135,10 @@ func TestTransactionToModelJSON(t *testing.T) {
Count: 6,
Sum: 7,
},
RepresentativeCount: 8,
Sampled: true,
Root: true,
RepresentativeCount: 8,
Sampled: true,
Root: true,
ProfilerStackTraceIds: []string{"foo", "foo", "bar"},
},
expectedMetricset: &modeljson.Transaction{
SpanCount: modeljson.SpanCount{
Expand Down Expand Up @@ -174,9 +176,10 @@ func TestTransactionToModelJSON(t *testing.T) {
Count: 6,
Sum: 7,
},
RepresentativeCount: 8,
Sampled: true,
Root: true,
RepresentativeCount: 8,
Sampled: true,
Root: true,
ProfilerStackTraceIds: []string{"foo", "foo", "bar"},
},
},
}
Expand Down
Loading

0 comments on commit 668fa48

Please sign in to comment.