Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add feature of refreshing service root spans #172

Merged
merged 3 commits into from
Dec 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions go.work.sum
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ cloud.google.com/go/workflows v1.12.8/go.mod h1:b7akG38W6lHmyPc+WYJxIYl1rEv79bBM
dario.cat/mergo v1.0.0/go.mod h1:uNxQE+84aUszobStD9th8a29P2fMDhsBdgRYvZOxGmk=
dmitri.shuralyov.com/gpu/mtl v0.0.0-20221208032759-85de2813cf6b/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
git.sr.ht/~sbinet/gg v0.5.0/go.mod h1:G2C0eRESqlKhS7ErsNey6HHrqU1PwsnCQlekFi9Q2Oo=
github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8=
github.com/HdrHistogram/hdrhistogram-go v1.1.2/go.mod h1:yDgFjdqOqDEKOvasDdhWNXYg9BVp4O+o5f6V/ehm6Oo=
github.com/IBM/sarama v1.43.2/go.mod h1:Kyo4WkF24Z+1nz7xeVUFWIuKVV8RS3wM8mkvPKMdXFQ=
github.com/IBM/sarama v1.43.3/go.mod h1:FVIRaLrhK3Cla/9FfRF5X9Zua2KpS3SYIXxhac1H+FQ=
Expand Down Expand Up @@ -261,8 +262,10 @@ github.com/cilium/ebpf v0.11.0/go.mod h1:WE7CZAnqOL2RouJ4f1uyNhqr2P4CCvXFIqdRDUg
github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50/go.mod h1:5e1+Vvlzido69INQaVO6d87Qn543Xr6nooe9Kz7oBFM=
github.com/containerd/cgroups/v3 v3.0.3/go.mod h1:8HBe7V3aWGLFPd/k03swSIsGjZhHI2WzJmticMgVuz0=
github.com/containerd/containerd v1.7.15/go.mod h1:ISzRRTMF8EXNpJlTzyr2XMhN+j9K302C21/+cr3kUnY=
github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw=
github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/dockercfg v0.3.1/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/crossdock/crossdock-go v0.0.0-20160816171116-049aabb0122b/go.mod h1:v9FBN7gdVTpiD/+LZ7Po0UKvROyT87uLVxTHVky/dlQ=
github.com/cyphar/filepath-securejoin v0.2.5/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4=
Expand Down Expand Up @@ -352,6 +355,7 @@ github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YO
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
github.com/moby/sys/sequential v0.5.0/go.mod h1:tH2cOOs5V9MlPiXcQzRC+eEyab644PWKGRYaaV5ZZlo=
github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU=
github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28=
github.com/montanaflynn/stats v0.7.0/go.mod h1:etXPPgVO6n31NxCd9KQUMvCM+ve0ruNzt6R8Bnaayow=
github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
github.com/nsf/jsondiff v0.0.0-20230430225905-43f6cf3098c1/go.mod h1:mpRZBD8SJ55OIICQ3iWH0Yz3cjzA61JdqMLoWXeB2+8=
Expand Down Expand Up @@ -388,6 +392,7 @@ github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden v0.108.0/go
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden v0.109.0/go.mod h1:P7e6ch+uoSfxK+lMwfcndkHE6gWUqvWKpr7mD04KIAA=
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden v0.110.0/go.mod h1:eazQnU3D7Xwyctq8Yfig4ws5HcnD3G+ygWrG9H03JjE=
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden v0.112.0/go.mod h1:G4KniRkewEl7JaT1EVTczTWi1nfYk2bD5GAn4aqBh4o=
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden v0.115.0/go.mod h1:mtxUxJEIQy27MaGR1yzcn/OK8NoddEgb7fumpEbKYss=
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/kafka/topic v0.111.0/go.mod h1:Lqc5Me0HU2a/DAESI//0UuKnGszTwuDnV+oVOLRkfio=
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/ottl v0.111.0/go.mod h1:GQHN6IbBsaGmMJIOQcqA7RXiJi55rXldP3di5YJ1IYA=
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.106.1/go.mod h1:St0VVFKzA0fNxo5RmzI4fg7ucGttd840OZ56a+ZECZs=
Expand All @@ -396,6 +401,7 @@ github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.108.0
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.109.0/go.mod h1:KvJWxR0bDk9Qh0ktw4gOFsd/ZrJ7p5KTAQueEJsaK9Q=
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.110.0/go.mod h1:nn/ktwLdZ6O9mtnRuak8NQEfGRow3VI3l+YqAroJX7g=
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.112.0/go.mod h1:dQCrspUDJRs7P6pXRALwj/yKIMzTYCvLa7XlzNycVFY=
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.115.0/go.mod h1:R8AkVWe9G5Q0oMOapvm9HNS076E3Min8SVlmhBL3QD0=
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.106.1/go.mod h1:ehzaiDdkrww7l1Stvse5GCOAsAZOpFcgeIbB/2PqFs4=
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.107.0/go.mod h1:/RtBag3LuHIkqN4bo8Erd3jCzA3gea70l9WyJ9TncXM=
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatautil v0.110.0 h1:KYBzbgQyCz4i5zjzs0iBOFuNh2vagaw2seqvZ7Lftxk=
Expand Down Expand Up @@ -463,6 +469,7 @@ github.com/spf13/cast v1.6.0/go.mod h1:ancEpBxwJDODSW/UG4rDrAqiKolqNNh2DX3mk86cA
github.com/spf13/viper v1.19.0/go.mod h1:GQUN9bilAbhU/jgc1bKs99f/suXKeUMct8Adx5+Ntkg=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/testcontainers/testcontainers-go v0.31.0/go.mod h1:D2lAoA0zUFiSY+eAflqK5mcUx/A5hrrORaEQrd0SefI=
github.com/testcontainers/testcontainers-go v0.34.0/go.mod h1:6P/kMkQe8yqPHfPWNulFGdFHTD8HB2vLq/231xY2iPQ=
github.com/tilinna/clock v1.1.0/go.mod h1:ZsP7BcY7sEEz7ktc0IVy8Us6boDrK8VradlKRUGfOao=
github.com/ua-parser/uap-go v0.0.0-20240611065828-3a4781585db6/go.mod h1:BUbeWZiieNxAuuADTBNb3/aeje6on3DhU3rpWsQSB1E=
github.com/uber/jaeger-client-go v2.30.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
Expand Down Expand Up @@ -549,6 +556,7 @@ go.opentelemetry.io/collector/processor/memorylimiterprocessor v0.106.1/go.mod h
go.opentelemetry.io/collector/processor/memorylimiterprocessor v0.111.0/go.mod h1:s42Gm7LMqietFs0Cpl+ma2sEYZP3RWHIlXlWimGW2cQ=
go.opentelemetry.io/collector/receiver/otlpreceiver v0.110.0 h1:F1TsgyWimClOvAfP7nUbKChPfoNt8tThI64WNCutkAE=
go.opentelemetry.io/collector/receiver/otlpreceiver v0.110.0/go.mod h1:MuLDagbS+v3iUwMSN/iJPWiDMLPEYxeYolzc+T2ElU0=
go.opentelemetry.io/collector/scraper v0.115.0/go.mod h1:7YoCO6/4PeExLiX1FokcydJGCQUa7lUqZsqXokJ5VZ4=
go.opentelemetry.io/collector/semconv v0.110.0 h1:KHQnOHe3gUz0zsxe8ph9kN5OTypCFD4V+06AiBTfeNk=
go.opentelemetry.io/collector/semconv v0.110.0/go.mod h1:zCJ5njhWpejR+A40kiEoeFm1xq1uzyZwMnRNX6/D82A=
go.opentelemetry.io/collector/service v0.110.0 h1:jeGdUi+5HQuH0Ho/Gd+VusY77MYJAUxUm0Vxh7aNbjQ=
Expand Down Expand Up @@ -595,8 +603,6 @@ golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5D
golang.org/x/crypto v0.25.0/go.mod h1:T+wALwcMOSE0kXgUAnPAHqTLW+XHgcELELW8VaDgm/M=
golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A=
golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
golang.org/x/exp/shiny v0.0.0-20230817173708-d852ddb80c63/go.mod h1:UH99kUObWAZkDnWqppdQe5ZhPYESUw8I0zVV1uWBR+0=
golang.org/x/mod v0.9.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
golang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
Expand Down
23 changes: 21 additions & 2 deletions tuiexporter/internal/telemetry/cache.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,17 @@ type TraceServiceSpanDataMap map[string]map[string][]*SpanData
// the spans have any error status
type TraceServiceHasErrorMap map[string]map[string]bool

// TraceServiceParentIDMap is a map of trace id and service name to a parent span id
// This is used to update service root spans in the trace list.
type TraceServiceParentIDMap map[string]map[string]*SpanData

// TraceCache is a cache of trace spans
type TraceCache struct {
spanid2span SpanDataMap
traceid2spans TraceSpanDataMap
tracesvc2spans TraceServiceSpanDataMap
tracesvc2haserror TraceServiceHasErrorMap
tracesvc2parent TraceServiceParentIDMap
}

// NewTraceCache returns a new trace cache
Expand All @@ -33,34 +38,46 @@ func NewTraceCache() *TraceCache {
traceid2spans: TraceSpanDataMap{},
tracesvc2spans: TraceServiceSpanDataMap{},
tracesvc2haserror: TraceServiceHasErrorMap{},
tracesvc2parent: TraceServiceParentIDMap{},
}
}

// UpdateCache updates the cache with a new span
func (c *TraceCache) UpdateCache(sname string, data *SpanData) (newtracesvc bool) {
func (c *TraceCache) UpdateCache(sname string, data *SpanData) (newtracesvc bool, replaceSpanID string) {
c.spanid2span[data.Span.SpanID().String()] = data
traceID := data.Span.TraceID().String()
hasError := spanHasError(data.Span)
if ts, ok := c.traceid2spans[traceID]; ok {
c.traceid2spans[traceID] = append(ts, data)
if _, ok := c.tracesvc2spans[traceID][sname]; ok {
c.tracesvc2spans[traceID][sname] = append(c.tracesvc2spans[traceID][sname], data)
if c.tracesvc2parent[traceID][sname].Span.ParentSpanID().String() == data.Span.SpanID().String() {
// This span is higher parent span
// NOTE: In this process, for performance reasons, only adjacent parent-child relationships
// between spans are evaluated. For example, if the parent-child order of spans is 1, 2, 3, and
// the arrival order is 3, 1, 2, span 2 will be recognized as the service root span. To recalculate
// the specific parent-child relationship, use `R` key to trigger deep refreshing
replaceSpanID = c.tracesvc2parent[traceID][sname].Span.SpanID().String()
c.tracesvc2parent[traceID][sname] = data
}
if hasError {
c.tracesvc2haserror[traceID][sname] = hasError
}
} else {
c.tracesvc2spans[traceID][sname] = []*SpanData{data}
c.tracesvc2haserror[traceID][sname] = hasError
c.tracesvc2parent[traceID][sname] = data
newtracesvc = true
}
} else {
c.traceid2spans[traceID] = []*SpanData{data}
c.tracesvc2spans[traceID] = map[string][]*SpanData{sname: {data}}
c.tracesvc2haserror[traceID] = map[string]bool{sname: hasError}
c.tracesvc2parent[traceID] = map[string]*SpanData{sname: data}
newtracesvc = true
}

return newtracesvc
return newtracesvc, replaceSpanID
}

// DeleteCache deletes a list of spans from the cache
Expand All @@ -77,9 +94,11 @@ func (c *TraceCache) DeleteCache(serviceSpans []*SpanData) {
}
delete(c.tracesvc2spans[traceID], sname.AsString())
delete(c.tracesvc2haserror[traceID], sname.AsString())
delete(c.tracesvc2parent[traceID], sname.AsString())
if len(c.tracesvc2spans[traceID]) == 0 {
delete(c.tracesvc2spans, traceID)
delete(c.tracesvc2haserror, traceID)
delete(c.tracesvc2parent, traceID)
// delete spans in traceid2spans only if there are no spans left in tracesvc2spans
// for better performance
delete(c.traceid2spans, traceID)
Expand Down
49 changes: 48 additions & 1 deletion tuiexporter/internal/telemetry/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,15 @@ func (sd *SpanData) IsRoot() bool {
// This is a slice of one span of a single service
type SvcSpans []*SpanData

func (ss *SvcSpans) replaceBySpanID(replaceSpanID string, data *SpanData) {
for i, s := range *ss {
if s.Span.SpanID().String() == replaceSpanID {
(*ss)[i] = data
return
}
}
}

// MetricData is a struct to represent a metric
type MetricData struct {
Metric *pmetric.Metric
Expand Down Expand Up @@ -249,6 +258,41 @@ func (s *Store) GetFilteredServiceSpansByIdx(idx int) []*SpanData {
return spans
}

// RecalculateServiceRootSpanByIdx recalculates service root span of the specified index
func (s *Store) RecalculateServiceRootSpanByIdx(idx int) {
s.mut.Lock()
defer func() {
s.updatedAt = time.Now()
s.mut.Unlock()
}()

if idx < 0 || idx >= len(s.svcspansFiltered) {
return
}
traceID := s.svcspansFiltered[idx].Span.TraceID().String()
currentSpanID := s.svcspansFiltered[idx].Span.SpanID().String()
sname, ok := s.svcspansFiltered[idx].ResourceSpan.Resource().Attributes().Get("service.name")
if !ok {
return
}

spans := s.tracecache.tracesvc2spans[traceID][sname.AsString()]
spanMemo := make(map[string]bool)
for _, span := range spans {
spanMemo[span.Span.SpanID().String()] = true
}
for _, span := range spans {
parentSpanID := span.Span.ParentSpanID().String()
spanID := span.Span.SpanID().String()
if _, ok := spanMemo[parentSpanID]; !ok {
// TODO: Condider orphan span?
sd := s.tracecache.spanid2span[spanID]
s.svcspansFiltered[idx] = sd
s.svcspans.replaceBySpanID(currentSpanID, sd)
}
}
}

// GetFilteredMetricByIdx returns the metric at the given index
func (s *Store) GetFilteredMetricByIdx(idx int) *MetricData {
if idx < 0 || idx >= len(s.metricsFiltered) {
Expand Down Expand Up @@ -291,9 +335,12 @@ func (s *Store) AddSpan(traces *ptrace.Traces) {
ScopeSpans: &ss,
ReceivedAt: time.Now(),
}
newtracesvc := s.tracecache.UpdateCache(sname.AsString(), sd)
newtracesvc, replaceSpanID := s.tracecache.UpdateCache(sname.AsString(), sd)
if newtracesvc {
s.svcspans = append(s.svcspans, sd)
} else if len(replaceSpanID) > 0 {
// FIXME: More efficient logic is needed
s.svcspans.replaceBySpanID(replaceSpanID, sd)
}
}
}
Expand Down
123 changes: 123 additions & 0 deletions tuiexporter/internal/telemetry/store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -362,6 +362,129 @@ func TestStoreAddSpanWithRotation(t *testing.T) {
}
}

func TestStoreAddSpanServiceSpanCalculation(t *testing.T) {
// traceid: 1
// └- resource: test-service-1
// └- scope: test-scope-1-1
// └- span: span-1-1-2
// └- span: span-1-1-3
// traceid: 1 (the same trace)
// └- resource: test-service-1
// └- scope: test-scope-1-1
// └- span: span-1-1-1
store := NewStore()
store.maxServiceSpanCount = 1
payload1, _ := test.GenerateOTLPTracesPayload(t, 1, 1, []int{3}, [][]int{{3, 0, 0}})
payload1.ResourceSpans().At(0).ScopeSpans().At(0).Spans().At(1).SetParentSpanID(
payload1.ResourceSpans().At(0).ScopeSpans().At(0).Spans().At(0).SpanID(),
)
payload1.ResourceSpans().At(0).ScopeSpans().At(0).Spans().At(2).SetParentSpanID(
payload1.ResourceSpans().At(0).ScopeSpans().At(0).Spans().At(1).SpanID(),
)
payload1.ResourceSpans().At(0).ScopeSpans().At(0).Spans().RemoveIf(func(s ptrace.Span) bool {
return s.Name() == "span-0-0-0"
})
payload2, _ := test.GenerateOTLPTracesPayload(t, 1, 1, []int{3}, [][]int{{3, 0, 0}})
payload2.ResourceSpans().At(0).ScopeSpans().At(0).Spans().At(1).SetParentSpanID(
payload2.ResourceSpans().At(0).ScopeSpans().At(0).Spans().At(0).SpanID(),
)
payload2.ResourceSpans().At(0).ScopeSpans().At(0).Spans().At(2).SetParentSpanID(
payload2.ResourceSpans().At(0).ScopeSpans().At(0).Spans().At(1).SpanID(),
)
payload2.ResourceSpans().At(0).ScopeSpans().At(0).Spans().RemoveIf(func(s ptrace.Span) bool {
return s.Name() == "span-0-0-1" || s.Name() == "span-0-0-2"
})

assert.Equal(t, 2, payload1.ResourceSpans().At(0).ScopeSpans().At(0).Spans().Len())
assert.Equal(t, 1, payload2.ResourceSpans().At(0).ScopeSpans().At(0).Spans().Len())

store.AddSpan(&payload1)

// The service root span should be span-1-1-2
assert.Equal(t, 1, len(store.svcspans))
assert.Equal(t, "span-0-0-1", store.svcspans[0].Span.Name())

store.AddSpan(&payload2)

// Now, The service root span should be span-1-1-1
assert.Equal(t, 1, len(store.svcspans))
assert.Equal(t, "span-0-0-0", store.svcspans[0].Span.Name())
}

func TestStoreAddSpanServiceSpanCalculationLimitation(t *testing.T) {
// traceid: 1
// └- resource: test-service-1
// └- scope: test-scope-1-1
// └- span: span-1-1-3
// traceid: 1 (the same trace)
// └- resource: test-service-1
// └- scope: test-scope-1-1
// └- span: span-1-1-1
// traceid: 1 (the same trace)
// └- resource: test-service-1
// └- scope: test-scope-1-1
// └- span: span-1-1-2
store := NewStore()
store.maxServiceSpanCount = 1
payload1, _ := test.GenerateOTLPTracesPayload(t, 1, 1, []int{3}, [][]int{{3, 0, 0}})
payload1.ResourceSpans().At(0).ScopeSpans().At(0).Spans().At(1).SetParentSpanID(
payload1.ResourceSpans().At(0).ScopeSpans().At(0).Spans().At(0).SpanID(),
)
payload1.ResourceSpans().At(0).ScopeSpans().At(0).Spans().At(2).SetParentSpanID(
payload1.ResourceSpans().At(0).ScopeSpans().At(0).Spans().At(1).SpanID(),
)
payload1.ResourceSpans().At(0).ScopeSpans().At(0).Spans().RemoveIf(func(s ptrace.Span) bool {
return s.Name() == "span-0-0-0" || s.Name() == "span-0-0-1"
})
payload2, _ := test.GenerateOTLPTracesPayload(t, 1, 1, []int{3}, [][]int{{3, 0, 0}})
payload2.ResourceSpans().At(0).ScopeSpans().At(0).Spans().At(1).SetParentSpanID(
payload2.ResourceSpans().At(0).ScopeSpans().At(0).Spans().At(0).SpanID(),
)
payload2.ResourceSpans().At(0).ScopeSpans().At(0).Spans().At(2).SetParentSpanID(
payload2.ResourceSpans().At(0).ScopeSpans().At(0).Spans().At(1).SpanID(),
)
payload2.ResourceSpans().At(0).ScopeSpans().At(0).Spans().RemoveIf(func(s ptrace.Span) bool {
return s.Name() == "span-0-0-1" || s.Name() == "span-0-0-2"
})
payload3, _ := test.GenerateOTLPTracesPayload(t, 1, 1, []int{3}, [][]int{{3, 0, 0}})
payload3.ResourceSpans().At(0).ScopeSpans().At(0).Spans().At(1).SetParentSpanID(
payload3.ResourceSpans().At(0).ScopeSpans().At(0).Spans().At(0).SpanID(),
)
payload3.ResourceSpans().At(0).ScopeSpans().At(0).Spans().At(2).SetParentSpanID(
payload3.ResourceSpans().At(0).ScopeSpans().At(0).Spans().At(1).SpanID(),
)
payload3.ResourceSpans().At(0).ScopeSpans().At(0).Spans().RemoveIf(func(s ptrace.Span) bool {
return s.Name() == "span-0-0-0" || s.Name() == "span-0-0-2"
})

assert.Equal(t, 1, payload1.ResourceSpans().At(0).ScopeSpans().At(0).Spans().Len())
assert.Equal(t, 1, payload2.ResourceSpans().At(0).ScopeSpans().At(0).Spans().Len())
assert.Equal(t, 1, payload3.ResourceSpans().At(0).ScopeSpans().At(0).Spans().Len())

store.AddSpan(&payload1)

// The service root span should be span-1-1-3
assert.Equal(t, 1, len(store.svcspans))
assert.Equal(t, "span-0-0-2", store.svcspans[0].Span.Name())

store.AddSpan(&payload2)

// The service root span should still be span-1-1-3
assert.Equal(t, 1, len(store.svcspans))
assert.Equal(t, "span-0-0-2", store.svcspans[0].Span.Name())

store.AddSpan(&payload3)

// Finally, The service root span should be span-1-1-2
assert.Equal(t, 1, len(store.svcspans))
assert.Equal(t, "span-0-0-1", store.svcspans[0].Span.Name())

// By RecalculateServiceRootSpanByIdx, we can get span-1-1-1 as the root span
store.RecalculateServiceRootSpanByIdx(0)
assert.Equal(t, 1, len(store.svcspans))
assert.Equal(t, "span-0-0-0", store.svcspans[0].Span.Name())
}

func TestStoreAddMetricWithoutRotation(t *testing.T) {
// metric: 1
// └- resource: test-service-1
Expand Down
12 changes: 12 additions & 0 deletions tuiexporter/internal/tui/component/page.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,14 @@ func (p *TUIPages) createTracePage(store *telemetry.Store) *tview.Flex {
}
log.Printf("sortType: %s", sortType)
store.ApplyFilterTraces(inputConfirmed, sortType)
return nil
} else if event.Rune() == 'r' {
row, _ := table.GetSelection()
if row == 0 {
return nil
}
store.RecalculateServiceRootSpanByIdx(row - 1)

return nil
}
return event
Expand All @@ -208,6 +216,10 @@ func (p *TUIPages) createTracePage(store *telemetry.Store) *tview.Flex {
key: tcell.NewEventKey(tcell.KeyRune, 'S', tcell.ModCtrl),
description: "Toggle sort (Latency)",
},
{
key: tcell.NewEventKey(tcell.KeyRune, 'R', tcell.ModNone),
description: "Recalculate service root span",
},
{
key: tcell.NewEventKey(tcell.KeyRune, 'L', tcell.ModCtrl),
description: "Clear all data",
Expand Down
Loading