Skip to content

Commit 6dbbc4c

Browse files
authored
Make service target backward compatible with destination resource (#7925)
* Deprecate span.context.destination.service.resource
1 parent 776f1b8 commit 6dbbc4c

File tree

13 files changed

+1202
-9
lines changed

13 files changed

+1202
-9
lines changed

beater/test_approved_es_documents/TestPublishIntegrationSpans.approved.json

Lines changed: 582 additions & 1 deletion
Large diffs are not rendered by default.

changelogs/head.asciidoc

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ https://github.com/elastic/apm-server/compare/8.2\...main[View commits]
99

1010
[float]
1111
==== Deprecations
12+
- `span.context.destination.service.resource` is deprecated {pull}7925[7925]
1213

1314
[float]
1415
==== Bug fixes
@@ -18,6 +19,7 @@ https://github.com/elastic/apm-server/compare/8.2\...main[View commits]
1819
==== Intake API Changes
1920
- For OpenTelemetry exception span events, stack traces that cannot be parsed will now be stored in `event.stack_trace` {pull}7706[7706]
2021
- Support for ingesting `service.target.type` and `service.target.name` added to intake API for spans {pull}7870[7870]
22+
- Derive `service.target.{type, name}` fields for older agents from `span.context.destination.service.resource` {pull}7925[7925]
2123

2224
[float]
2325
==== Added

docs/spec/rumv3/span.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@
5555
"maxLength": 1024
5656
},
5757
"rc": {
58-
"description": "Resource identifies the destination service resource being operated on e.g. 'http://elastic.co:80', 'elasticsearch', 'rabbitmq/queue_name'",
58+
"description": "Resource identifies the destination service resource being operated on e.g. 'http://elastic.co:80', 'elasticsearch', 'rabbitmq/queue_name' DEPRECATED: this field will be removed in a future release",
5959
"type": "string",
6060
"maxLength": 1024
6161
},

docs/spec/rumv3/transaction.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -659,7 +659,7 @@
659659
"maxLength": 1024
660660
},
661661
"rc": {
662-
"description": "Resource identifies the destination service resource being operated on e.g. 'http://elastic.co:80', 'elasticsearch', 'rabbitmq/queue_name'",
662+
"description": "Resource identifies the destination service resource being operated on e.g. 'http://elastic.co:80', 'elasticsearch', 'rabbitmq/queue_name' DEPRECATED: this field will be removed in a future release",
663663
"type": "string",
664664
"maxLength": 1024
665665
},

docs/spec/v2/span.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -147,7 +147,7 @@
147147
"maxLength": 1024
148148
},
149149
"resource": {
150-
"description": "Resource identifies the destination service resource being operated on e.g. 'http://elastic.co:80', 'elasticsearch', 'rabbitmq/queue_name'",
150+
"description": "Resource identifies the destination service resource being operated on e.g. 'http://elastic.co:80', 'elasticsearch', 'rabbitmq/queue_name' DEPRECATED: this field will be removed in a future release",
151151
"type": "string",
152152
"maxLength": 1024
153153
},

model/modeldecoder/rumv3/model.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -409,6 +409,7 @@ type spanContextDestinationService struct {
409409
Name nullable.String `json:"n" validate:"maxLength=1024"`
410410
// Resource identifies the destination service resource being operated on
411411
// e.g. 'http://elastic.co:80', 'elasticsearch', 'rabbitmq/queue_name'
412+
// DEPRECATED: this field will be removed in a future release
412413
Resource nullable.String `json:"rc" validate:"required,maxLength=1024"`
413414
// Type of the destination service, e.g. db, elasticsearch. Should
414415
// typically be the same as span.type.

model/modeldecoder/v2/decoder.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"io"
2424
"net/http"
2525
"net/textproto"
26+
"regexp"
2627
"strconv"
2728
"strings"
2829
"sync"
@@ -67,6 +68,13 @@ var (
6768
}
6869
)
6970

71+
var (
72+
// reForServiceTargetExpr regex will capture service target type and name
73+
// Service target type comprises of only lowercase alphabets
74+
// Service target name comprises of all word characters
75+
reForServiceTargetExpr = regexp.MustCompile(`^([a-z]+)(?:/(\w+))?$`)
76+
)
77+
7078
func fetchErrorRoot() *errorRoot {
7179
return errorRootPool.Get().(*errorRoot)
7280
}
@@ -968,6 +976,10 @@ func mapToSpanModel(from *span, event *model.APMEvent) {
968976
mapToServiceModel(from.Context.Service, &event.Service)
969977
mapToAgentModel(from.Context.Service.Agent, &event.Agent)
970978
}
979+
if !from.Context.Service.Target.Type.IsSet() && from.Context.Destination.Service.Resource.IsSet() {
980+
outTarget := targetFromDestinationResource(from.Context.Destination.Service.Resource.Val)
981+
event.Service.Target = &outTarget
982+
}
971983
if len(from.Context.Tags) > 0 {
972984
modeldecoderutil.MergeLabels(from.Context.Tags, event)
973985
}
@@ -1434,3 +1446,15 @@ func mapSpanLinks(from []spanLink, out *[]model.SpanLink) {
14341446
}
14351447
}
14361448
}
1449+
1450+
func targetFromDestinationResource(res string) (target model.ServiceTarget) {
1451+
submatch := reForServiceTargetExpr.FindStringSubmatch(res)
1452+
switch len(submatch) {
1453+
case 3:
1454+
target.Type = submatch[1]
1455+
target.Name = submatch[2]
1456+
default:
1457+
target.Name = res
1458+
}
1459+
return
1460+
}

model/modeldecoder/v2/model.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -812,6 +812,7 @@ type spanContextDestinationService struct {
812812
Name nullable.String `json:"name" validate:"maxLength=1024"`
813813
// Resource identifies the destination service resource being operated on
814814
// e.g. 'http://elastic.co:80', 'elasticsearch', 'rabbitmq/queue_name'
815+
// DEPRECATED: this field will be removed in a future release
815816
Resource nullable.String `json:"resource" validate:"required,maxLength=1024"`
816817
// Type of the destination service, e.g. db, elasticsearch. Should
817818
// typically be the same as span.type.

model/modeldecoder/v2/span_test.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ func TestDecodeMapToSpanModel(t *testing.T) {
9898
// Kind is tested further down
9999
"Kind",
100100

101+
// Derived using service.target.*
102+
"DestinationService.Resource",
103+
101104
// Not set for spans:
102105
"DestinationService.ResponseTime",
103106
"DestinationService.ResponseTime.Count",
@@ -491,4 +494,43 @@ func TestDecodeMapToSpanModel(t *testing.T) {
491494
Trace: model.Trace{ID: "trace2"},
492495
}}, out.Span.Links)
493496
})
497+
498+
t.Run("service-target", func(t *testing.T) {
499+
for _, tc := range []struct {
500+
name string
501+
inTargetType, inTargetName string
502+
outTargetType, outTargetName string
503+
resource string
504+
}{
505+
{name: "passed-as-input", inTargetType: "some-type", outTargetType: "some-type", outTargetName: ""},
506+
{name: "infer-from-resource", resource: "postgres/testdb", outTargetType: "postgres", outTargetName: "testdb"},
507+
{name: "infer-only-type-from-resource", resource: "mysql", outTargetType: "mysql", outTargetName: ""},
508+
{name: "infer-only-name-from-resource", resource: "my-db", outTargetType: "", outTargetName: "my-db"},
509+
} {
510+
t.Run(tc.name, func(t *testing.T) {
511+
var input span
512+
defaultVal := modeldecodertest.DefaultValues()
513+
modeldecodertest.SetStructValues(&input, defaultVal)
514+
if tc.inTargetType != "" {
515+
input.Context.Service.Target.Type.Set(tc.inTargetType)
516+
} else {
517+
input.Context.Service.Target.Type.Reset()
518+
}
519+
if tc.inTargetName != "" {
520+
input.Context.Service.Target.Name.Set(tc.inTargetName)
521+
} else {
522+
input.Context.Service.Target.Name.Reset()
523+
}
524+
if tc.resource != "" {
525+
input.Context.Destination.Service.Resource.Set(tc.resource)
526+
} else {
527+
input.Context.Destination.Service.Resource.Reset()
528+
}
529+
var out model.APMEvent
530+
mapToSpanModel(&input, &out)
531+
assert.Equal(t, tc.outTargetType, out.Service.Target.Type)
532+
assert.Equal(t, tc.outTargetName, out.Service.Target.Name)
533+
})
534+
}
535+
})
494536
}

model/span.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ type DB struct {
8080
type DestinationService struct {
8181
Type string // Deprecated
8282
Name string // Deprecated
83-
Resource string
83+
Resource string // Deprecated
8484

8585
// ResponseTime holds aggregated span durations for the destination service resource.
8686
ResponseTime AggregatedDuration

0 commit comments

Comments
 (0)