diff --git a/cmd/query/app/querysvc/adjuster/spanlinks.go b/cmd/query/app/querysvc/adjuster/spanlinks.go new file mode 100644 index 00000000000..bb93dc1f9c3 --- /dev/null +++ b/cmd/query/app/querysvc/adjuster/spanlinks.go @@ -0,0 +1,50 @@ +// Copyright (c) 2024 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package adjuster + +import ( + "go.opentelemetry.io/collector/pdata/ptrace" +) + +// SpanLinks creates an adjuster that removes span links with empty trace IDs. +func SpanLinks() Adjuster { + return Func(func(traces ptrace.Traces) (ptrace.Traces, error) { + adjuster := linksAdjuster{} + resourceSpans := traces.ResourceSpans() + for i := 0; i < resourceSpans.Len(); i++ { + rs := resourceSpans.At(i) + scopeSpans := rs.ScopeSpans() + for j := 0; j < scopeSpans.Len(); j++ { + ss := scopeSpans.At(j) + spans := ss.Spans() + for k := 0; k < spans.Len(); k++ { + span := spans.At(k) + adjuster.adjust(span) + } + } + } + return traces, nil + }) +} + +type linksAdjuster struct{} + +// adjust removes invalid links from a span. +func (l linksAdjuster) adjust(span ptrace.Span) { + links := span.Links() + validLinks := ptrace.NewSpanLinkSlice() + for i := 0; i < links.Len(); i++ { + link := links.At(i) + if l.valid(link) { + newLink := validLinks.AppendEmpty() + link.CopyTo(newLink) + } + } + validLinks.CopyTo(span.Links()) +} + +// valid checks if a span link's TraceID is not empty. +func (linksAdjuster) valid(link ptrace.SpanLink) bool { + return !link.TraceID().IsEmpty() +} diff --git a/cmd/query/app/querysvc/adjuster/spanlinks_test.go b/cmd/query/app/querysvc/adjuster/spanlinks_test.go new file mode 100644 index 00000000000..9d64b5c8531 --- /dev/null +++ b/cmd/query/app/querysvc/adjuster/spanlinks_test.go @@ -0,0 +1,39 @@ +// Copyright (c) 2024 The Jaeger Authors. +// SPDX-License-Identifier: Apache-2.0 + +package adjuster + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "go.opentelemetry.io/collector/pdata/pcommon" + "go.opentelemetry.io/collector/pdata/ptrace" +) + +func TestLinksAdjuster(t *testing.T) { + trace := ptrace.NewTraces() + resourceSpans := trace.ResourceSpans().AppendEmpty() + scopeSpans := resourceSpans.ScopeSpans().AppendEmpty() + + // span with no links + scopeSpans.Spans().AppendEmpty() + + // span with empty traceID link + spanA := scopeSpans.Spans().AppendEmpty() + spanA.Links().AppendEmpty().SetTraceID(pcommon.NewTraceIDEmpty()) + + // span with 2 non-empty traceID links and 1 empty (or zero) traceID link + spanB := scopeSpans.Spans().AppendEmpty() + spanB.Links().AppendEmpty().SetTraceID(pcommon.TraceID([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1})) + spanB.Links().AppendEmpty().SetTraceID(pcommon.TraceID([]byte{0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0})) + spanB.Links().AppendEmpty().SetTraceID(pcommon.TraceID([]byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0})) + + trace, err := SpanLinks().Adjust(trace) + spans := trace.ResourceSpans().At(0).ScopeSpans().At(0).Spans() + require.NoError(t, err) + assert.Equal(t, 0, spans.At(0).Links().Len()) + assert.Equal(t, 0, spans.At(1).Links().Len()) + assert.Equal(t, 2, spans.At(2).Links().Len()) +}