Skip to content
7 changes: 6 additions & 1 deletion enrichments/trace/internal/elastic/span.go
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ type spanEnrichmentContext struct {
messagingSystem string
messagingDestinationName string
genAiSystem string
typeValue string

// The inferred* attributes are derived from a base attribute
userAgentOriginal string
Expand Down Expand Up @@ -200,6 +201,8 @@ func (s *spanEnrichmentContext) Enrich(
s.userAgentName = v.Str()
case semconv27.AttributeUserAgentVersion:
s.userAgentVersion = v.Str()
case "type":
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How will this attribute be set?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's set by the EDOT mobile SDKs specified here.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe also add elasticattr.TransactionType do this case so that if transaction.type is set already, it's not overridden?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe also add elasticattr.TransactionType do this case so that if transaction.type is set already, it's not overridden?

I'm ok with validating that transaction.type won't change if it's already present. It's a different use case though it seems like it'd be easy enough to add it in this PR. What do you think, @gregkalapos @lahsivjar ?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe also add elasticattr.TransactionType do this case so that if transaction.type is set already, it's not overridden?

I think overriding behaviour should be handled for other attributes too, but I am not sure if not overriding in all cases would be the correct option. I have created #185 to discuss it further.

Copy link
Contributor

@gregkalapos gregkalapos Apr 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok by me to include it in this PR. update: @lahsivjar was faster - let's discuss in #185.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think overriding behaviour should be handled for other attributes too, but I am not sure if not overriding in all cases would be the correct option. I have created #185 to discuss it further.

Got it. Thanks, @lahsivjar. I won't add that change here then.

Copy link
Contributor

@lahsivjar lahsivjar Apr 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we should do it in a separate PR since this concern might apply to other attributes too, but I don't have a strong opinion here. Does that makes sense @gregkalapos @felixbarny ? Or do we think only transaction.type needs to be handled this way?

NVM, let's discuss in #185

s.typeValue = v.Str()
}
return true
})
Expand Down Expand Up @@ -319,7 +322,7 @@ func (s *spanEnrichmentContext) enrichSpan(
s.setUserAgentIfRequired(span)
}

if isExitRootSpan && transactionTypeEnabled {
if isExitRootSpan && transactionTypeEnabled && s.typeValue == "" {
if spanType != "" {
transactionType := spanType
if spanSubtype != "" {
Expand Down Expand Up @@ -351,6 +354,8 @@ func (s *spanEnrichmentContext) getSampled() bool {
func (s *spanEnrichmentContext) getTxnType() string {
txnType := "unknown"
switch {
case s.typeValue != "":
txnType = s.typeValue
case s.isMessaging:
txnType = "messaging"
case s.isRPC, s.isHTTP:
Expand Down
95 changes: 95 additions & 0 deletions enrichments/trace/internal/elastic/span_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,11 @@ func TestElasticTransactionEnrich(t *testing.T) {
span.SetEndTimestamp(endTs)
return span
}
getElasticMobileTxn := func() ptrace.Span {
span := getElasticTxn()
span.Attributes().PutStr("type", "mobile")
return span
}
for _, tc := range []struct {
name string
input ptrace.Span
Expand Down Expand Up @@ -422,6 +427,58 @@ func TestElasticTransactionEnrich(t *testing.T) {
semconv27.AttributeUserAgentVersion: "51.0.2704",
},
},
{
name: "with_type",
input: func() ptrace.Span {
span := ptrace.NewSpan()
span.Attributes().PutStr("type", "mobile")
return span
}(),
config: config.Enabled().Transaction,
enrichedAttrs: map[string]any{
elasticattr.TimestampUs: int64(0),
elasticattr.TransactionSampled: true,
elasticattr.TransactionRoot: true,
elasticattr.TransactionID: "",
elasticattr.TransactionName: "",
elasticattr.ProcessorEvent: "transaction",
elasticattr.TransactionRepresentativeCount: float64(1),
elasticattr.TransactionDurationUs: int64(0),
elasticattr.EventOutcome: "success",
elasticattr.SuccessCount: int64(1),
elasticattr.TransactionResult: "Success",
elasticattr.TransactionType: "mobile",
},
},
{
name: "all_disabled_for_mobile",
input: getElasticMobileTxn(),
enrichedAttrs: map[string]any{},
},
{
name: "http_status_ok_for_mobile",
input: func() ptrace.Span {
span := getElasticMobileTxn()
span.SetName("testtxn")
span.Attributes().PutInt(semconv25.AttributeHTTPStatusCode, http.StatusOK)
return span
}(),
config: config.Enabled().Transaction,
enrichedAttrs: map[string]any{
elasticattr.TimestampUs: startTs.AsTime().UnixMicro(),
elasticattr.TransactionSampled: true,
elasticattr.TransactionRoot: true,
elasticattr.TransactionID: "0100000000000000",
elasticattr.TransactionName: "testtxn",
elasticattr.ProcessorEvent: "transaction",
elasticattr.TransactionRepresentativeCount: float64(1),
elasticattr.TransactionDurationUs: expectedDuration.Microseconds(),
elasticattr.EventOutcome: "success",
elasticattr.SuccessCount: int64(1),
elasticattr.TransactionResult: "HTTP 2xx",
elasticattr.TransactionType: "mobile",
},
},
} {
t.Run(tc.name, func(t *testing.T) {
expectedSpan := ptrace.NewSpan()
Expand Down Expand Up @@ -605,6 +662,44 @@ func TestRootSpanAsDependencyEnrich(t *testing.T) {
elasticattr.SpanRepresentativeCount: float64(1),
},
},
{
name: "outgoing_mobile_http_root_span",
input: func() ptrace.Span {
span := ptrace.NewSpan()
span.SetName("rootClientSpan")
span.SetSpanID([8]byte{1})
span.SetKind(ptrace.SpanKindClient)
span.Attributes().PutStr("type", "mobile")
span.Attributes().PutStr(semconv27.AttributeHTTPRequestMethod, "GET")
span.Attributes().PutStr(semconv27.AttributeURLFull, "http://localhost:8080")
span.Attributes().PutInt(semconv27.AttributeHTTPResponseStatusCode, 200)
span.Attributes().PutStr(semconv27.AttributeNetworkProtocolVersion, "1.1")
return span
}(),
config: config.Enabled(),
enrichedAttrs: map[string]any{
elasticattr.TimestampUs: int64(0),
elasticattr.TransactionName: "rootClientSpan",
elasticattr.ProcessorEvent: "transaction",
elasticattr.SpanType: "external",
elasticattr.SpanSubtype: "http",
elasticattr.SpanDestinationServiceResource: "localhost:8080",
elasticattr.SpanName: "rootClientSpan",
elasticattr.EventOutcome: "success",
elasticattr.SuccessCount: int64(1),
elasticattr.ServiceTargetName: "localhost:8080",
elasticattr.ServiceTargetType: "http",
elasticattr.TransactionID: "0100000000000000",
elasticattr.TransactionDurationUs: int64(0),
elasticattr.TransactionRepresentativeCount: float64(1),
elasticattr.TransactionResult: "HTTP 2xx",
elasticattr.TransactionType: "mobile",
elasticattr.TransactionSampled: true,
elasticattr.TransactionRoot: true,
elasticattr.SpanDurationUs: int64(0),
elasticattr.SpanRepresentativeCount: float64(1),
},
},
} {
t.Run(tc.name, func(t *testing.T) {
expectedSpan := ptrace.NewSpan()
Expand Down
Loading