Skip to content

Commit d0310d1

Browse files
committed
Support composite spans in the intake API
1 parent 46f214e commit d0310d1

File tree

11 files changed

+394
-2
lines changed

11 files changed

+394
-2
lines changed

beater/test_approved_es_documents/TestPublishIntegrationSpans.approved.json

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -893,6 +893,146 @@
893893
"id": "123",
894894
"name": "john"
895895
}
896+
},
897+
{
898+
"@timestamp": "2021-07-06T11:58:05.682Z",
899+
"agent": {
900+
"name": "elastic-node",
901+
"version": "3.14.0"
902+
},
903+
"cloud": {
904+
"account": {
905+
"id": "account_id",
906+
"name": "account_name"
907+
},
908+
"availability_zone": "cloud_availability_zone",
909+
"instance": {
910+
"id": "instance_id",
911+
"name": "instance_name"
912+
},
913+
"machine": {
914+
"type": "machine_type"
915+
},
916+
"project": {
917+
"id": "project_id",
918+
"name": "project_name"
919+
},
920+
"provider": "cloud_provider",
921+
"region": "cloud_region",
922+
"service": {
923+
"name": "lambda"
924+
}
925+
},
926+
"container": {
927+
"id": "container-id"
928+
},
929+
"ecs": {
930+
"version": "1.10.0"
931+
},
932+
"event": {
933+
"outcome": "success"
934+
},
935+
"host": {
936+
"architecture": "x64",
937+
"hostname": "node-name",
938+
"ip": "127.0.0.1",
939+
"name": "node-name",
940+
"os": {
941+
"platform": "darwin"
942+
}
943+
},
944+
"kubernetes": {
945+
"namespace": "namespace1",
946+
"node": {
947+
"name": "node-name"
948+
},
949+
"pod": {
950+
"name": "pod-name",
951+
"uid": "pod-uid"
952+
}
953+
},
954+
"labels": {
955+
"tag1": "label1"
956+
},
957+
"observer": {
958+
"ephemeral_id": "00000000-0000-0000-0000-000000000000",
959+
"hostname": "",
960+
"id": "fbba762a-14dd-412c-b7e9-b79f903eb492",
961+
"type": "test-apm-server",
962+
"version": "1.2.3",
963+
"version_major": 1
964+
},
965+
"parent": {
966+
"id": "abcdef0123456789"
967+
},
968+
"process": {
969+
"args": [
970+
"node",
971+
"server.js"
972+
],
973+
"pid": 1234,
974+
"ppid": 6789,
975+
"title": "node"
976+
},
977+
"processor": {
978+
"event": "span",
979+
"name": "transaction"
980+
},
981+
"service": {
982+
"environment": "staging",
983+
"framework": {
984+
"name": "Express",
985+
"version": "1.2.3"
986+
},
987+
"language": {
988+
"name": "ecmascript",
989+
"version": "8"
990+
},
991+
"name": "backendspans",
992+
"node": {
993+
"name": "container-id"
994+
},
995+
"runtime": {
996+
"name": "node",
997+
"version": "8.0.0"
998+
},
999+
"version": "5.1.3"
1000+
},
1001+
"span": {
1002+
"action": "query",
1003+
"composite": {
1004+
"compression_strategy": "exact_match",
1005+
"count": 10,
1006+
"sum": {
1007+
"us": 32592
1008+
}
1009+
},
1010+
"duration": {
1011+
"us": 3781
1012+
},
1013+
"id": "abcdef01234567",
1014+
"name": "SELECT FROM p_details",
1015+
"start": {
1016+
"us": 2830
1017+
},
1018+
"subtype": "postgresql",
1019+
"type": "db"
1020+
},
1021+
"timestamp": {
1022+
"us": 1625572685682272
1023+
},
1024+
"trace": {
1025+
"id": "edcbaf0123456789abcdef9876543210"
1026+
},
1027+
"transaction": {
1028+
"id": "01af25874dec69dd"
1029+
},
1030+
"user": {
1031+
"domain": "ldap://abc",
1032+
"email": "s@test.com",
1033+
"id": "123",
1034+
"name": "john"
1035+
}
8961036
}
8971037
]
8981038
}

docs/spec/v2/span.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,34 @@
2222
},
2323
"minItems": 0
2424
},
25+
"composite": {
26+
"description": "Composite holds details on a group of spans represented by a single one.",
27+
"type": [
28+
"null",
29+
"object"
30+
],
31+
"properties": {
32+
"compression_strategy": {
33+
"description": "A string value indicating which compression strategy was used. The valid values are `exact_match` and `same_kind`.",
34+
"type": "string"
35+
},
36+
"count": {
37+
"description": "Count is the number of compressed spans the composite span represents. The minimum count is 2, as a composite span represents at least two spans.",
38+
"type": "integer",
39+
"minimum": 2
40+
},
41+
"sum": {
42+
"description": "Sum is the durations of all compressed spans this composite span represents in milliseconds. Thus sum is the net duration of all the compressed spans while duration is the gross duration, including \"whitespace\" between the spans.",
43+
"type": "number",
44+
"minimum": 0
45+
}
46+
},
47+
"required": [
48+
"count",
49+
"sum",
50+
"compression_strategy"
51+
]
52+
},
2553
"context": {
2654
"description": "Context holds arbitrary contextual information for the event.",
2755
"type": [

model/modeldecoder/rumv3/transaction_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,7 @@ func TestDecodeMapToTransactionModel(t *testing.T) {
223223
"Metadata",
224224
// values not set for RUM v3
225225
"ChildIDs",
226+
"Composite",
226227
"DB",
227228
"Experimental",
228229
"HTTP.Response.Headers",

model/modeldecoder/v2/decoder.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -770,6 +770,19 @@ func mapToSpanModel(from *span, metadata *model.Metadata, reqTime time.Time, con
770770
out.Type = from.Type.Val
771771
}
772772
}
773+
if from.Composite.IsSet() {
774+
composite := model.Composite{}
775+
if from.Composite.Count.IsSet() {
776+
composite.Count = from.Composite.Count.Val
777+
}
778+
if from.Composite.Sum.IsSet() {
779+
composite.Sum = from.Composite.Sum.Val
780+
}
781+
if from.Composite.CompressionStrategy.IsSet() {
782+
composite.CompressionStrategy = from.Composite.CompressionStrategy.Val
783+
}
784+
out.Composite = &composite
785+
}
773786
if len(from.ChildIDs) > 0 {
774787
out.ChildIDs = make([]string, len(from.ChildIDs))
775788
copy(out.ChildIDs, from.ChildIDs)

model/modeldecoder/v2/model.go

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -605,6 +605,8 @@ type span struct {
605605
Action nullable.String `json:"action" validate:"maxLength=1024"`
606606
// ChildIDs holds a list of successor transactions and/or spans.
607607
ChildIDs []string `json:"child_ids" validate:"maxLength=1024"`
608+
// Composite holds details on a group of spans represented by a single one.
609+
Composite spanComposite `json:"composite"`
608610
// Context holds arbitrary contextual information for the event.
609611
Context spanContext `json:"context"`
610612
// Duration of the span in milliseconds
@@ -768,6 +770,20 @@ type stacktraceFrame struct {
768770
_ struct{} `validate:"requiredAnyOf=classname;filename"`
769771
}
770772

773+
type spanComposite struct {
774+
// Count is the number of compressed spans the composite span represents.
775+
// The minimum count is 2, as a composite span represents at least two spans.
776+
Count nullable.Int `json:"count" validate:"required,min=2"`
777+
// Sum is the durations of all compressed spans this composite span
778+
// represents in milliseconds. Thus sum is the net duration of all the
779+
// compressed spans while duration is the gross duration, including
780+
// "whitespace" between the spans.
781+
Sum nullable.Float64 `json:"sum" validate:"required,min=0"`
782+
// A string value indicating which compression strategy was used. The valid
783+
// values are `exact_match` and `same_kind`.
784+
CompressionStrategy nullable.String `json:"compression_strategy" validate:"required"`
785+
}
786+
771787
type transaction struct {
772788
// Context holds arbitrary contextual information for the event.
773789
Context context `json:"context"`

model/modeldecoder/v2/model_generated.go

Lines changed: 37 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

model/modeldecoder/v2/model_test.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -508,6 +508,8 @@ func TestSpanRequiredValidationRules(t *testing.T) {
508508
var event span
509509
modeldecodertest.InitStructValues(&event)
510510
event.Outcome.Set("failure")
511+
// Composite.Count must be > 1
512+
event.Composite.Count.Set(2)
511513
// test vanilla struct is valid
512514
require.NoError(t, event.validate())
513515

@@ -521,6 +523,9 @@ func TestSpanRequiredValidationRules(t *testing.T) {
521523
"parent_id": nil,
522524
"trace_id": nil,
523525
"type": nil,
526+
"composite.count": nil,
527+
"composite.sum": nil,
528+
"composite.compression_strategy": nil,
524529
}
525530
cb := assertRequiredFn(t, requiredKeys, event.validate)
526531
modeldecodertest.SetZeroStructValue(&event, cb)

model/span.go

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ type Span struct {
6868
URL string
6969
Destination *Destination
7070
DestinationService *DestinationService
71+
Composite *Composite
7172

7273
Experimental interface{}
7374

@@ -102,6 +103,13 @@ type DestinationService struct {
102103
Resource string
103104
}
104105

106+
// Composite holds details on a group of spans compressed into one.
107+
type Composite struct {
108+
Count int
109+
Sum float64
110+
CompressionStrategy string
111+
}
112+
105113
func (db *DB) fields() common.MapStr {
106114
if db == nil {
107115
return nil
@@ -143,6 +151,18 @@ func (d *DestinationService) fields() common.MapStr {
143151
return common.MapStr(fields)
144152
}
145153

154+
func (c *Composite) fields() common.MapStr {
155+
if c == nil {
156+
return nil
157+
}
158+
var fields mapStr
159+
fields.set("count", c.Count)
160+
fields.set("sum", utility.MillisAsMicros(c.Sum))
161+
fields.set("compression_strategy", c.CompressionStrategy)
162+
163+
return common.MapStr(fields)
164+
}
165+
146166
func (e *Span) toBeatEvent(ctx context.Context) beat.Event {
147167
spanTransformations.Inc()
148168
if frames := len(e.Stacktrace); frames > 0 {
@@ -214,6 +234,7 @@ func (e *Span) fields(ctx context.Context) common.MapStr {
214234
}
215235
fields.maybeSetMapStr("db", e.DB.fields())
216236
fields.maybeSetMapStr("message", e.Message.Fields())
237+
fields.maybeSetMapStr("composite", e.Composite.fields())
217238
if destinationServiceFields := e.DestinationService.fields(); len(destinationServiceFields) > 0 {
218239
common.MapStr(fields).Put("destination.service", destinationServiceFields)
219240
}

model/span_test.go

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,8 @@ func TestSpanTransform(t *testing.T) {
118118
Name: destServiceName,
119119
Resource: destServiceResource,
120120
},
121-
Message: &Message{QueueName: "users"},
121+
Message: &Message{QueueName: "users"},
122+
Composite: &Composite{Count: 10, Sum: 1.1, CompressionStrategy: "exact_match"},
122123
},
123124
Output: common.MapStr{
124125
"span": common.MapStr{
@@ -153,6 +154,11 @@ func TestSpanTransform(t *testing.T) {
153154
},
154155
},
155156
"message": common.MapStr{"queue": common.MapStr{"name": "users"}},
157+
"composite": common.MapStr{
158+
"count": 10,
159+
"sum": common.MapStr{"us": 1100},
160+
"compression_strategy": "exact_match",
161+
},
156162
},
157163
"labels": common.MapStr{"label_a": 12, "label_b": "b", "c": 1},
158164
"processor": common.MapStr{"event": "span", "name": "transaction"},

0 commit comments

Comments
 (0)