From 961311f278c5f3a862d539ac0e22cb6e93d89001 Mon Sep 17 00:00:00 2001 From: Alejandro Do Nascimento Mora Date: Fri, 30 Sep 2022 15:15:58 +0200 Subject: [PATCH] Preserve Jaeger RefType when storing/retrieving traces. Jaeger, like OpenTracing, uses identifies the type of references between Spans. There are only two types of references `CHILD_OF` and `FOLLOWS_FROM`. OTEL doesn't have an analogous to Jaeger reference type in their link specification. An OTEL span can have only one parent, and that's set as an attributed of the span instead of an OTEL link. When using the Jaeger to OTEL translator the first reference with type `CHILD_OF` is used to set the OTEL span parent attribute, all other references are converted to links, and since there is no notion of reference type that information is lost. On the reverse transformation, OTEL to Jaeger, the OTEL Span parent is converted to a reference with `CHILD_OF` type, while all the other OTEL links are converted to `FOLLOWS_FROM` references. The problem is that Jaeger, unlike OTEL, supports the notion of multiple parents (one use case is fork/join workloads), running this multi-parent through the translator and back returns a span with a single parent with all the other parents turned into `FOLLOWS_FROM` references. There's an open PR in the translator to keep this information by adding the type as attribute to the links following the semantic convention for OpenTracing compatibility: https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/compatibility/#OpenTracing https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/14463 While that gets merge we are going to be manually setting the reference type as an attribute on the links when writing traces. On the retrieving traces side, we'll be using those attributes to set the corresponding reference type when constructing the Jaeger traces responses. --- CHANGELOG.md | 2 + pkg/jaeger/store/find_traces.go | 74 +++++++++++++++- pkg/jaeger/store/store.go | 64 ++++++++++++++ .../generate_jaeger_response_test.go | 23 ++--- .../end_to_end_tests/ingest_trace_test.go | 83 ++++++++++++++---- .../end_to_end_tests/jaeger_store_test.go | 8 +- pkg/tests/testdata/jaeger_query_responses.sz | Bin 4661 -> 4666 bytes pkg/tests/testdata/trace_data.go | 18 +++- 8 files changed, 227 insertions(+), 45 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 88dbe6c18b..d667986d5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -43,6 +43,8 @@ We use the following categories for changes: ingested using the native Jaeger API. - Fix traces queries returning duplicated events and links (or logs and references in Jaeger) for traces with more than one event and one link. +- Fix incorrect reference types when retrieving Jaeger traces with multiple + parent references [#1681]. ## [0.14.0] - 2022-08-30 diff --git a/pkg/jaeger/store/find_traces.go b/pkg/jaeger/store/find_traces.go index dc97ed5cdc..c5ee66c9bb 100644 --- a/pkg/jaeger/store/find_traces.go +++ b/pkg/jaeger/store/find_traces.go @@ -13,6 +13,7 @@ import ( jaegertranslator "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger" "github.com/timescale/promscale/pkg/pgxconn" "go.opentelemetry.io/collector/pdata/ptrace" + conventions "go.opentelemetry.io/collector/semconv/v1.9.0" ) func findTraces(ctx context.Context, builder *Builder, conn pgxconn.PgxConn, q *spanstore.TraceQueryParameters) ([]*model.Trace, error) { @@ -54,10 +55,10 @@ func scanTraces(rows pgxconn.PgxRows) ([]*model.Trace, error) { return nil, fmt.Errorf("internal-traces-to-jaeger-proto: %w", err) } - return batchSliceToTraceSlice(batch), nil + return batchSliceToTraceSlice(batch, getOtherParents(traces)), nil } -func batchSliceToTraceSlice(bSlice []*model.Batch) []*model.Trace { +func batchSliceToTraceSlice(bSlice []*model.Batch, otherParents map[model.SpanID][]int) []*model.Trace { // Mostly Copied from Jaeger's grpc_client.go // https://github.com/jaegertracing/jaeger/blob/067dff713ab635ade66315bbd05518d7b28f40c6/plugin/storage/grpc/shared/grpc_client.go#L179 traces := make([]*model.Trace, 0) @@ -75,8 +76,77 @@ func batchSliceToTraceSlice(bSlice []*model.Batch) []*model.Trace { //copy over the process from the batch span.Process = batch.Process decodeSpanBinaryTags(span) + + // TODO: There's an open PR against the Jaeger translator that adds + // support for keeping the RefType. Once the PR is merged we can remove + // the following if condition and the getOtherParents function. + // + // https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/14463 + if pIdxs, ok := otherParents[span.SpanID]; ok { + refs := span.GetReferences() + for _, i := range pIdxs { + refs[i].RefType = model.ChildOf + } + } trace.Spans = append(trace.Spans, span) } } return traces } + +// getOtherParents returns a map where the keys are the IDs of Spans that have +// more than one parent and the values are the position in the Span.References +// list where those other parents references are. +// +// A parent is a link that has the `child_of` attribute defined in the semantic +// convention for opentracing: +// +// https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/compatibility/#opentracing +// +// It tracks the position instead of the SpanID because there might multiple +// links to the same SpanID but different RefTypes. +func getOtherParents(traces ptrace.Traces) map[model.SpanID][]int { + otherParents := map[model.SpanID][]int{} + + resourceSpans := traces.ResourceSpans() + for i := 0; i < resourceSpans.Len(); i++ { + rSpan := resourceSpans.At(i) + sSpans := rSpan.ScopeSpans() + for j := 0; j < sSpans.Len(); j++ { + sSpan := sSpans.At(j) + spans := sSpan.Spans() + if spans.Len() == 0 { + continue + } + for k := 0; k < spans.Len(); k++ { + span := spans.At(k) + links := span.Links() + + // We need an offset because if the span has a ParentSpanID, then + // that's going to be the first link when translating from OTEL to + // Jaeger. We could say that is it doesn't have a ParentSpanID then + // it shouldn't have other parents, but just to be extra safe we + // inspect the attributes even if there's no ParentSpanID set. + offset := 0 + if !span.ParentSpanID().IsEmpty() { + offset = 1 + } + for l := 0; l < links.Len(); l++ { + link := links.At(l) + v, ok := link.Attributes().Get(conventions.AttributeOpentracingRefType) + if !ok || v.StringVal() != conventions.AttributeOpentracingRefTypeChildOf { + continue + } + spanID := spanIDToJaegerProto(span.SpanID().Bytes()) + pIdxs, ok := otherParents[spanID] + if !ok { + pIdxs = []int{} + otherParents[spanID] = pIdxs + } + otherParents[spanID] = append(pIdxs, l+offset) + } + } + } + } + return otherParents +} diff --git a/pkg/jaeger/store/store.go b/pkg/jaeger/store/store.go index 7cf3730ea5..a68bfbe999 100644 --- a/pkg/jaeger/store/store.go +++ b/pkg/jaeger/store/store.go @@ -6,6 +6,7 @@ package store import ( "context" + "encoding/binary" "time" "github.com/pkg/errors" @@ -16,6 +17,8 @@ import ( "github.com/jaegertracing/jaeger/storage/spanstore" jaegertranslator "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger" + "go.opentelemetry.io/collector/pdata/ptrace" + conventions "go.opentelemetry.io/collector/semconv/v1.9.0" "github.com/timescale/promscale/pkg/log" "github.com/timescale/promscale/pkg/pgmodel/ingestor" @@ -60,9 +63,70 @@ func (p *Store) WriteSpan(ctx context.Context, span *model.Span) error { if err != nil { return err } + + // TODO: There's an open PR against the Jaeger translator that adds support + // for keeping the RefType. Once the PR is merged we can remove the following + // if condition and the addRefTypeAttributeToLinks function. + // + // https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/14463 + if len(span.References) > 1 { + links := traces.ResourceSpans().At(0).ScopeSpans().At(0).Spans().At(0).Links() + addRefTypeAttributeToLinks(span, links) + } + return p.inserter.IngestTraces(ctx, traces) } +// TODO: There's an open PR against the Jaeger translator that adds support +// for keeping the RefType. Once the PR is merged we can delete this function. +// +// https://github.com/open-telemetry/opentelemetry-collector-contrib/pull/14463 +// +// addRefTypeAttributeToLinks adds the RefType of the Jaeger Span references as +// an attribute to their corresponding OTEL links. The `links` argument must +// be the OTEL representation of the given `span.References`. +// +// The added attributes follow the OpenTracing to OTEL semantic convention +// https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/compatibility/#opentracing +func addRefTypeAttributeToLinks(span *model.Span, links ptrace.SpanLinkSlice) { + + // The reference to the parent span is stored directly as an attribute + // of the Span and not as a Link. + parentsSpanID := span.ParentSpanID() + otherParentsSpanIDs := make(map[model.SpanID]struct{}) + + // Since there are only 2 types of refereces, ChildOf and FollowsFrom, we + // keep track only of the former. + for _, ref := range span.References { + if ref.RefType == model.ChildOf && ref.SpanID != parentsSpanID { + otherParentsSpanIDs[ref.SpanID] = struct{}{} + } + } + + for i := 0; i < links.Len(); i++ { + link := links.At(i) + spanID := spanIDToJaegerProto(link.SpanID().Bytes()) + + // Everything that's not ChildOf will be set as FollowsFrom. + if _, ok := otherParentsSpanIDs[spanID]; ok { + link.Attributes().InsertString( + conventions.AttributeOpentracingRefType, + conventions.AttributeOpentracingRefTypeChildOf, + ) + continue + } + link.Attributes().InsertString( + conventions.AttributeOpentracingRefType, + conventions.AttributeOpentracingRefTypeFollowsFrom, + ) + } +} + +// SpanIDToUInt64 converts the pcommon.SpanID to uint64 representation. +func spanIDToJaegerProto(rawSpanID [8]byte) model.SpanID { + return model.SpanID(binary.BigEndian.Uint64(rawSpanID[:])) +} + // Close performs graceful shutdown of SpanWriter on Jaeger collector shutdown. // In our case we have nothing to do // Noop impl avoid getting GRPC error message when Jaeger collector shuts down. diff --git a/pkg/tests/end_to_end_tests/generate_jaeger_response_test.go b/pkg/tests/end_to_end_tests/generate_jaeger_response_test.go index e3ab74f299..c4d4c77216 100644 --- a/pkg/tests/end_to_end_tests/generate_jaeger_response_test.go +++ b/pkg/tests/end_to_end_tests/generate_jaeger_response_test.go @@ -14,16 +14,12 @@ import ( "fmt" "testing" - "go.opentelemetry.io/collector/pdata/ptrace" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials/insecure" - + "github.com/jaegertracing/jaeger/model" jaegerproto "github.com/jaegertracing/jaeger/proto-gen/api_v2" - jaegertranslator "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger" - "github.com/stretchr/testify/require" "github.com/timescale/promscale/pkg/internal/testhelpers" - "github.com/timescale/promscale/pkg/tests/testdata" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" ) // TestGenerateJaegerAPIResponses is not an actual test, rather a function to spin up jaeger container @@ -36,8 +32,10 @@ func TestGenerateJaegerAPIResponses(t *testing.T) { jaeger, err := testhelpers.StartJaegerContainer(true) require.NoError(t, err) - sampleTraces := testdata.GenerateTestTrace() - err = insertDataIntoJaeger(fmt.Sprintf("localhost:%s", jaeger.GrpcReceivingPort.Port()), testdata.CopyTraces(sampleTraces)) + traceFixtures, err := getTracesFixtures() + require.NoError(t, err) + + err = insertDataIntoJaeger(fmt.Sprintf("localhost:%s", jaeger.GrpcReceivingPort.Port()), traceFixtures.batches) require.NoError(t, err) var store traceResponsesStore @@ -62,7 +60,7 @@ func TestGenerateJaegerAPIResponses(t *testing.T) { require.NoError(t, storeJaegerQueryResponses(&store)) } -func insertDataIntoJaeger(endpoint string, data ptrace.Traces) error { +func insertDataIntoJaeger(endpoint string, batches []*model.Batch) error { opts := []grpc.DialOption{grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithBlock()} conn, err := grpc.Dial(endpoint, opts...) if err != nil { @@ -71,11 +69,6 @@ func insertDataIntoJaeger(endpoint string, data ptrace.Traces) error { client := jaegerproto.NewCollectorServiceClient(conn) - batches, err := jaegertranslator.ProtoFromTraces(data) - if err != nil { - panic(err) - } - for _, batch := range batches { _, err := client.PostSpans(context.Background(), &jaegerproto.PostSpansRequest{Batch: *batch}, grpc.WaitForReady(true)) if err != nil { diff --git a/pkg/tests/end_to_end_tests/ingest_trace_test.go b/pkg/tests/end_to_end_tests/ingest_trace_test.go index af538182bd..08b16570b1 100644 --- a/pkg/tests/end_to_end_tests/ingest_trace_test.go +++ b/pkg/tests/end_to_end_tests/ingest_trace_test.go @@ -189,25 +189,23 @@ func getOperationsTest(t testing.TB, q *store.Store) { } } -// tracesFixtures contains an OTEL `traces` object to be used as a -// fixture. Also, each of the traces is present in the Jaeger model.Traces -// format so that they can be used to compare results from Jaeger query -// responses. +// tracesFixtures contains `traces` to be used as a fixture in both OTEL +// ptrace.Trace and Jager []*model.Batch representation. Also, each of +// the traces is present in the Jaeger model.Traces format so that they +// can be used to compare results from Jaeger query responses. type tracesFixtures struct { - traces ptrace.Traces - trace1 *model.Trace - trace2 *model.Trace + traces ptrace.Traces + batches []*model.Batch + trace1 *model.Trace + trace2 *model.Trace } -func getTracesFixtures() (tracesFixtures, error) { - - traces := testdata.GenerateTestTrace() - - fixtureBatch, err := jaegertranslator.ProtoFromTraces(traces.Clone()) +func tracesFixturesToBatches(traces ptrace.Traces) ([]*model.Batch, error) { + batches, err := jaegertranslator.ProtoFromTraces(traces) if err != nil { - return tracesFixtures{}, err + return nil, err } - for _, b := range fixtureBatch { + for _, b := range batches { for _, s := range b.Spans { // ProtoFromTraces doesn't populates span.Process because it is already been exposed by batch.Process. // See https://github.com/jaegertracing/jaeger-idl/blob/05fe64e9c305526901f70ff692030b388787e388/proto/api_v2/model.proto#L152-L160 @@ -221,19 +219,54 @@ func getTracesFixtures() (tracesFixtures, error) { } } + // We expect these spans references to be ChildOf because the fixture + // sets the Link attribute + // conventions.AttributeOpentracingRefType: conventions.AttributeOpentracingRefTypeChildOf + for _, span := range batches[0].Spans[4:] { + if span.GetOperationName() != "operationB" { + panic("invalid span") + } + span.GetReferences()[0].RefType = model.ChildOf + } + + return batches, nil +} + +func getTracesFixtures() (tracesFixtures, error) { + + traces := testdata.GenerateTestTrace() + batches, err := tracesFixturesToBatches(traces) + + if err != nil { + return tracesFixtures{}, err + } + // After passing the traces from testdata.GenerateTestTrace through the // translator we end up with 2 batches. The first one has 8 spans, the second // 4. The first 4 spans of the first batch belong to the same trace and the - // other 4 belong to the same trace as all the spans in the second batch. + // other 4 belong to the same trace as all the spans in the second batch, + // meaning that there are 2 traces. + // + // Batches are ordered by Process, the analogous to Resource in OTEL. + // + // Basically:: + // batches = [ + // [sT1, sT1, sT1, sT1, s1T2, s1T2, s1T2, s1T2], + // [s2T2, s2T2, s2T2, s2T2], + // ] + // + // Note: in the example there are just three types of spans sT1, s1T2 and + // s2T2, that's because each type shares the same attributes they just have + // unique Span IDs. trace1 := &model.Trace{ - Spans: fixtureBatch[0].Spans[:4], + Spans: batches[0].Spans[:4], ProcessMap: nil, Warnings: make([]string, 0), } trace2Spans := make([]*model.Span, 0) - trace2Spans = append(trace2Spans, fixtureBatch[0].Spans[4:]...) - trace2Spans = append(trace2Spans, fixtureBatch[1].Spans...) + trace2Spans = append(trace2Spans, batches[0].Spans[4:]...) + trace2Spans = append(trace2Spans, batches[1].Spans...) trace2 := &model.Trace{ Spans: trace2Spans, @@ -241,7 +274,19 @@ func getTracesFixtures() (tracesFixtures, error) { Warnings: make([]string, 0), } - return tracesFixtures{traces, trace1, trace2}, nil + // We recreate the batches to have a unique copy that can be + // modified without altering trace1 and trace2 + batches, err = tracesFixturesToBatches(traces.Clone()) + if err != nil { + return tracesFixtures{}, err + } + + return tracesFixtures{ + traces, + batches, + trace1, + trace2, + }, nil } func findTraceTest(t testing.TB, q *store.Store, fixtures tracesFixtures) { diff --git a/pkg/tests/end_to_end_tests/jaeger_store_test.go b/pkg/tests/end_to_end_tests/jaeger_store_test.go index 66a15ea072..1eee07c185 100644 --- a/pkg/tests/end_to_end_tests/jaeger_store_test.go +++ b/pkg/tests/end_to_end_tests/jaeger_store_test.go @@ -5,7 +5,6 @@ import ( "testing" "github.com/jackc/pgx/v4/pgxpool" - jaegertranslator "github.com/open-telemetry/opentelemetry-collector-contrib/pkg/translator/jaeger" "github.com/stretchr/testify/require" "github.com/timescale/promscale/pkg/jaeger/store" jaegerstore "github.com/timescale/promscale/pkg/jaeger/store" @@ -26,13 +25,8 @@ func TestJaegerSpanIngestion(t *testing.T) { if err != nil { require.NoError(t, err) } - batch, err := jaegertranslator.ProtoFromTraces(fixtures.traces) - require.NoError(t, err) - for _, b := range batch { + for _, b := range fixtures.batches { for _, s := range b.Spans { - // ProtoFromTraces doesn't populates span.Process because it is already been exposed by batch.Process. - // See https://github.com/jaegertracing/jaeger-idl/blob/05fe64e9c305526901f70ff692030b388787e388/proto/api_v2/model.proto#L152-L160 - s.Process = b.Process err = jaegerStore.SpanWriter().WriteSpan(context.Background(), s) require.NoError(t, err) } diff --git a/pkg/tests/testdata/jaeger_query_responses.sz b/pkg/tests/testdata/jaeger_query_responses.sz index f4cc6e3241ef082fbc77f3197823cbdc7c731ab3..71c72b5373b0f9ace2de1c8e999d86a0add5a023 100755 GIT binary patch literal 4666 zcmeHHU2Ggz6+UNYXLr`#UGK(r;&p5q)1+Ej?{@xwqQ#2sjj4@YJGN0+KcjQeHq5c%?`v0wjb|wL4t5B0@am z35h&d`ZPP*d(Qd3^UXOoP631un{%zBFuV);pZLwoSO3AB;o~P5#O^3CZ8A^7mfs9X zxDfdP!T2;FVrIU$dlBPr1Hgmvw-Si^mX=A=S&94=HeDcL6jsGLU}YHNrvT>SCuzCd z!9i#}1(-x`fdreLO+t*f06S(|Brqe-Z)OTfb`dZ1V0;EJ#da>zfEOsm?)bBmf?HY+ zqrhvfvbBLMz{ldxF<4cAOJbVU_D|L^3&<|COgvitI$&ll@F_!xN6Oy-5*_WL&1JaC2#WFk5 zFjqrN(c|7fWgj-z2|hp2F;84V0}f>P^OSiZGJ|N*qenc))i*w}8*|CzB2M(hU!vVl z`*;~HmVTXi9MZWza2S7W>@DcKCq73t6>@L&Zc;fL1MwFC4#uBn4*V_m4EF9v2Gf*e z@l=cbesuv$f87W0q4c)odoBPbFf;T(A&kuR*3i9usR@rX zoDe@f`UCE(45~fIC9remgzy>V6FdMPKc7UrXWeWq9f&J4$;tX;KGy{}szG+h8i2j= zHz{2<_r1fd(eIAFGz{&3J{F(GG$!)lpokNgPAMd?=1)lW)g1a0myqj*BKyf-bVG&R4}y8+ zFf;t~Cx1pi4NEp@Y;n2B4L!(~3Y8OHw8s8~itJ*iGkfNL1zaz5AG{aXH)ld@0Um2X zG7XuhIXbpx>G5;Sp@OO!vLo8ED4Mn-@_O52Y9>#oz>o}C*Id;wjMqIzCzjy|x+%+= zF7vWN!|sfAucld+#ml_r2t>41+1M_v3%a71j;`v0!_yyHx<=0pf$+SbYLwKr&aJd> zr0#%sZlrXoOtMwYc12N>RdUl~h*vWd(^X87j<;qgf@N+OH$~S~ZAaDwTh=94xU=dl zkIDbOgWD(Z{UaT~-~UE#u!r8TbU$hZ-j*yyR2);#bk#MgZ{+xKdU4^2+jS7s;9 zPeV;N70Yo&TXQL!EGr|E3?y#6ng!wVt67eY*si6{k7hpnc7Cv@js@Q3HC~iuT@@6| za?JPs&W&M0yalIU$?LpM_tjNR>b$Dz&h=OFHQsU+UNLk{F+_o=>PQWNYhTLW#?+UF+>v<3_P9AD9O>ks`LQ!m;3K50D zTQsPqN+Ru5a*0hZ3dhl|UrEab5p~O@)Pi8x!Uq-p^Z7%XC#kY+OOE9js&4VF_HW7$ z;(~ETR$R;AEz_~+1)=C;*;rLdnrdqBp+nS)swx!9Mdh>Rd?}v+9sgcbN+SgX9Y>1AiXrg61{2#Ao3XW`|omGe78Rd*pACP*l}RTfgK0_KMq_u)4uC* pA60m5eFr-Z+&vE5ytdv?XY$SZ4*pjTsMO5sd+&bSe|;~&zW@n|lfnQ1 literal 4661 zcmeHHU2Ggz6+UNYXLr_~UGK(rvW{&U)1lP}+ zL!OYxgQZV1qkGRe-#Oo$bCL`|bj+SzHY}{ZX?~MV%F-4J8)4LS)%D&w|D_CFS`I4Eaw_!amEg5Bkm~; z@?&*-Il>IP?*5Z_%wDDVbYI&n^(iyhmm?dDc`mlYcs^iPL=Z;T?%av_bb21g%E{+g z^^;A!3YU9+oqGVX`IiMuzHsm*DBqS`WH}Y{FLkf8bk_Qk&j9RCKF#g>YyMH}-isV& z8Oi*a7JsEWhdqDU1Mr^Yv&^LYq6<2$=90j4lFaCb3^?bX)6?d8l^ zKOZ)-~1Vdgy@>mb*AMJvljj-`vvJ z?DQ1Q00|L}U{TT)U-OA?*}5!wh73PD1mCwbU3U%DlL%1^SM>nj;Gda&?mGuZ@U4!~ zE^J4hhG8?rw9{^%K_k{x%=-1F%|3)QYz7Bdj37FEx)p@fLwhD#gKT2!T{Tg@s^$g-$5(ANpx0RQSCh z*}pi<4gL7RAFyA;({*NBS}X|z_wYT%%Bdh;;lIn0?BFMJyJmk5LN|2oza98rO-0xO zJlTSD7INnW*0y%f0~fgiMP0X4Pj*#Vwp~vq=4QavY{I(0QY_UpeBH9FYXN6c$MPi8 zR#n4PiK;QPn`1So8II!+l^C8xWmi|Ntpt`@Zgasv)_msrb^( zvwk?>3cqg?_9$fca2t@jUdZ=%u?@@SqgEuY;%Ktw*^*)EzEyo8PY$u=EgU*BF*&+4 zJ+?6kHPhA{&zD`pXKbpf4Nq{8y79aS($(ihfwkCYt=Hk;^`8_>VzN>7b(@K<>!!z!YQ*t1qFJV)S+YcRefR)^uz98ME{5YTELVCb!hb%$#rzaqbzQ}CJWDqn;v4^_e8(;v z=TyyiEaKRn!*+vazBbOSj+-WNRBE`UW9W)4IoDVqNB3AxvTm!AZD~YhwQg=?{tNeS zWNNzOo2qL_hUz+oYUnKDze;@DT;9kGpKW7BZDa;5*^@Oxc3hAA;kUV>r@NY5crjO2 zsq8Viw%9Q~1kHdb$^ZZW diff --git a/pkg/tests/testdata/trace_data.go b/pkg/tests/testdata/trace_data.go index 9c74958000..e084d7b633 100644 --- a/pkg/tests/testdata/trace_data.go +++ b/pkg/tests/testdata/trace_data.go @@ -13,6 +13,7 @@ import ( "github.com/golang/snappy" "go.opentelemetry.io/collector/pdata/pcommon" "go.opentelemetry.io/collector/pdata/ptrace" + conventions "go.opentelemetry.io/collector/semconv/v1.9.0" ) const TracesEntryCount = 5932 // All entries in the traces-dataset.sz file. @@ -41,7 +42,11 @@ var ( }, ) spanEventAttributes = pcommon.NewMapFromRaw(map[string]interface{}{"span-event-attr": "span-event-attr-val"}) - spanLinkAttributes = pcommon.NewMapFromRaw(map[string]interface{}{"span-link-attr": "span-link-attr-val"}) + spanLinkAttributes = pcommon.NewMapFromRaw(map[string]interface{}{ + "span-link-attr": "span-link-attr-val", + // In Jaeger terms this means that the span has 2 parents. + conventions.AttributeOpentracingRefType: conventions.AttributeOpentracingRefTypeChildOf, + }) ) func GetTraceId(bSlice [16]byte) string { @@ -60,6 +65,16 @@ func GenerateBrokenTestTraces() ptrace.Traces { return data } +// GenerateTestTrace returns 2 traces with the following characteristics: +// +// - There are only 3 span types, in the sense that each span type shares +// the same attributes, but each instance has a unique SpanID. They are +// referenced below as Span1, Span2 and Span3. +// - Trace 1 has 4 spans of type Span1. +// - Trace 2 has 4 spans of type Span2 and 4 of type Span3. +// - Span1 and Span2 belong to the same Resource and InstrumentationScope. +// - Span1 has 2 Events and 2 Links to random SpanIDs. +// - Span2 has 2 Links to random SpanIDs. func GenerateTestTrace() ptrace.Traces { rand.Seed(1) spanCount := 4 @@ -179,7 +194,6 @@ func fillSpanOne(span ptrace.Span) { ev1.SetName("event") ev1.SetDroppedAttributesCount(2) link0 := span.Links().AppendEmpty() - initSpanLinkAttributes(link0.Attributes()) link0.SetTraceID(pcommon.NewTraceID([16]byte{'1'})) link0.SetSpanID(pcommon.NewSpanID(generateRandSpanID())) link0.SetDroppedAttributesCount(4)