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 sorting by the span latency #162

Merged
merged 1 commit into from
Sep 19, 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
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ require (
github.com/hashicorp/nomad/api v0.0.0-20240717122358-3d93bd3778f3 // indirect
github.com/hashicorp/serf v0.10.1 // indirect
github.com/hetznercloud/hcloud-go/v2 v2.10.2 // indirect
github.com/icza/gox v0.0.0-20240829094117-5982a7a6cca1 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/ionos-cloud/sdk-go/v6 v6.1.11 // indirect
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,8 @@ github.com/hashicorp/serf v0.10.1/go.mod h1:yL2t6BqATOLGc5HF7qbFkTfXoPIY0WZdWHfE
github.com/hetznercloud/hcloud-go/v2 v2.10.2 h1:9gyTUPhfNbfbS40Spgij5mV5k37bOZgt8iHKCbfGs5I=
github.com/hetznercloud/hcloud-go/v2 v2.10.2/go.mod h1:xQ+8KhIS62W0D78Dpi57jsufWh844gUw1az5OUvaeq8=
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
github.com/icza/gox v0.0.0-20240829094117-5982a7a6cca1 h1:7i7BDcTpFl5LAllCe3lrWyUlNGHc5bC6TF5VLDQI0q4=
github.com/icza/gox v0.0.0-20240829094117-5982a7a6cca1/go.mod h1:VbcN86fRkkUMPX2ufM85Um8zFndLZswoIW1eYtpAcVk=
github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
Expand Down
4 changes: 4 additions & 0 deletions go.work.sum
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,7 @@ github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFP
github.com/edsrzf/mmap-go v1.1.0/go.mod h1:19H/e8pUPLicwkyNgOykDXkJ9F0MHE+Z52B8EIth78Q=
github.com/elastic/elastic-transport-go/v8 v8.6.0/go.mod h1:YLHer5cj0csTzNFXoNQ8qhtGY1GTvSqPnKWKaqQE3Hk=
github.com/elastic/go-elasticsearch/v8 v8.14.0/go.mod h1:WRvnlGkSuZyp83M2U8El/LGXpCjYLrvlkSgkAH4O5I4=
github.com/elastic/lunes v0.1.0/go.mod h1:xGphYIt3XdZRtyWosHQTErsQTd4OP1p9wsbVoHelrd4=
github.com/facette/natsort v0.0.0-20181210072756-2cd4dd1e2dcb/go.mod h1:bH6Xx7IW64qjjJq8M2u4dxNaBiDfKK+z/3eGDpXEQhc=
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
github.com/go-fonts/liberation v0.3.2/go.mod h1:N0QsDLVUQPy3UYg9XAc3Uh3UDMp2Z7M1o4+X98dXkmI=
Expand Down Expand Up @@ -327,6 +328,7 @@ github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfn
github.com/lightstep/go-expohisto v1.0.0/go.mod h1:xDXD0++Mu2FOaItXtdDfksfgxfV0z1TMPa+e/EUd0cs=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=
github.com/lyft/protoc-gen-star/v2 v2.0.3/go.mod h1:amey7yeodaJhXSbf/TlLvWiqQfLOSpEk//mLlc+axEk=
github.com/magefile/mage v1.15.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A=
github.com/magiconair/properties v1.8.7/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc=
github.com/moby/spdystream v0.2.0/go.mod h1:f7i0iNDQJ059oMTcWxx8MA/zKFIuD/lY+0GqbN2Wy8c=
Expand All @@ -352,9 +354,11 @@ github.com/open-telemetry/opentelemetry-collector-contrib/pkg/batchpersignal v0.
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden v0.106.1/go.mod h1:6MVXAX6OpG01Gb38KJUP/8APe2BCmGYtzKPOua05bTw=
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden v0.107.0/go.mod h1:qj9lEtkVjQUzZ7FdJTeDqqTUq9xVU9kE4F8zZnHFB9M=
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/golden v0.108.0/go.mod h1:3ku/cfl0FXMSc/dc9DGrhABhE6/AoYArKtl3I9QEp28=
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/pdatatest v0.106.1/go.mod h1:St0VVFKzA0fNxo5RmzI4fg7ucGttd840OZ56a+ZECZs=
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.107.0/go.mod h1:oG/PliNiIOUHVARyDrFdvxFvG8DUPEjMGlmxjEqeoKM=
github.com/open-telemetry/opentelemetry-collector-contrib/pkg/pdatatest v0.108.0/go.mod h1:G+N43ID1sP2CnffxkYdMyuJpep2UcGQUyq4HiAmcYSw=
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/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/resourcetotelemetry v0.106.1/go.mod h1:qsYqM+UGm3h2M+MQnaKBeQmsBC+sIGgGNJyyQFfUCUI=
Expand Down
60 changes: 60 additions & 0 deletions tuiexporter/internal/telemetry/sort.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package telemetry

import "sort"

const (
SORT_TYPE_NONE SortType = "none"
SORT_TYPE_LATENCY_DESC SortType = "latency-desc"
SORT_TYPE_LATENCY_ASC SortType = "latency-asc"
)

// SortType is sort type
type SortType string

func (t SortType) IsNone() bool {
return t == SORT_TYPE_NONE
}

func (t SortType) IsDesc() bool {
return t == SORT_TYPE_LATENCY_DESC
}

func (t SortType) GetHeaderLabel() string {
switch t {
case SORT_TYPE_LATENCY_DESC:
return "Latency"
case SORT_TYPE_LATENCY_ASC:
return "Latency"
}
return "N/A"
}

func sortSvcSpans(svcSpans SvcSpans, sortType SortType) {
switch sortType {
case SORT_TYPE_NONE:
sort.Slice(svcSpans, func(i, j int) bool {
// default sort is received_at asc
return svcSpans[i].ReceivedAt.Before(svcSpans[j].ReceivedAt)
})
case SORT_TYPE_LATENCY_DESC:
sort.Slice(svcSpans, func(i, j int) bool {
istart := svcSpans[i].Span.StartTimestamp().AsTime()
iend := svcSpans[i].Span.EndTimestamp().AsTime()
iduration := iend.Sub(istart)
jstart := svcSpans[j].Span.StartTimestamp().AsTime()
jend := svcSpans[j].Span.EndTimestamp().AsTime()
jduration := jend.Sub(jstart)
return iduration > jduration
})
case SORT_TYPE_LATENCY_ASC:
sort.Slice(svcSpans, func(i, j int) bool {
istart := svcSpans[i].Span.StartTimestamp().AsTime()
iend := svcSpans[i].Span.EndTimestamp().AsTime()
iduration := iend.Sub(istart)
jstart := svcSpans[j].Span.StartTimestamp().AsTime()
jend := svcSpans[j].Span.EndTimestamp().AsTime()
jduration := jend.Sub(jstart)
return iduration < jduration
})
}
}
150 changes: 150 additions & 0 deletions tuiexporter/internal/telemetry/sort_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package telemetry

import (
"testing"
"time"

"github.com/stretchr/testify/assert"
"github.com/ymtdzzz/otel-tui/tuiexporter/internal/test"
)

func TestSortType(t *testing.T) {
tests := []struct {
name string
input SortType
wantIsNone bool
wantIsDesc bool
wantHeaderLabel string
}{
{
name: "SORT_TYPE_NONE",
input: SORT_TYPE_NONE,
wantIsNone: true,
wantIsDesc: false,
wantHeaderLabel: "N/A",
},
{
name: "SORT_TYPE_LATENCY_DESC",
input: SORT_TYPE_LATENCY_DESC,
wantIsNone: false,
wantIsDesc: true,
wantHeaderLabel: "Latency",
},
{
name: "SORT_TYPE_LATENCY_ASC",
input: SORT_TYPE_LATENCY_ASC,
wantIsNone: false,
wantIsDesc: false,
wantHeaderLabel: "Latency",
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equal(t, tt.wantIsNone, tt.input.IsNone())
assert.Equal(t, tt.wantIsDesc, tt.input.IsDesc())
assert.Equal(t, tt.wantHeaderLabel, tt.input.GetHeaderLabel())
})
}
}

func TestSortSvcSpans(t *testing.T) {
baseSvcSpans := SvcSpans{
&SpanData{
Span: test.GenerateSpanWithDuration(t, "100ms", 100*time.Millisecond),
},
&SpanData{
Span: test.GenerateSpanWithDuration(t, "75µs", 50*time.Microsecond),
},
&SpanData{
Span: test.GenerateSpanWithDuration(t, "230ms", 230*time.Millisecond),
},
&SpanData{
Span: test.GenerateSpanWithDuration(t, "101ms", 101*time.Millisecond),
},
&SpanData{
Span: test.GenerateSpanWithDuration(t, "50ns", 50*time.Nanosecond),
},
}

tests := []struct {
name string
sortType SortType
input SvcSpans
want SvcSpans
}{
{
name: "SORT_TYPE_NONE",
sortType: SORT_TYPE_NONE,
input: append(SvcSpans{}, baseSvcSpans...),
want: SvcSpans{
&SpanData{
Span: test.GenerateSpanWithDuration(t, "100ms", 100*time.Millisecond),
},
&SpanData{
Span: test.GenerateSpanWithDuration(t, "75µs", 50*time.Microsecond),
},
&SpanData{
Span: test.GenerateSpanWithDuration(t, "230ms", 230*time.Millisecond),
},
&SpanData{
Span: test.GenerateSpanWithDuration(t, "101ms", 101*time.Millisecond),
},
&SpanData{
Span: test.GenerateSpanWithDuration(t, "50ns", 50*time.Nanosecond),
},
},
},
{
name: "SORT_TYPE_LATENCY_DESC",
sortType: SORT_TYPE_LATENCY_DESC,
input: append(SvcSpans{}, baseSvcSpans...),
want: SvcSpans{
&SpanData{
Span: test.GenerateSpanWithDuration(t, "230ms", 230*time.Millisecond),
},
&SpanData{
Span: test.GenerateSpanWithDuration(t, "101ms", 101*time.Millisecond),
},
&SpanData{
Span: test.GenerateSpanWithDuration(t, "100ms", 100*time.Millisecond),
},
&SpanData{
Span: test.GenerateSpanWithDuration(t, "75µs", 50*time.Microsecond),
},
&SpanData{
Span: test.GenerateSpanWithDuration(t, "50ns", 50*time.Nanosecond),
},
},
},
{
name: "SORT_TYPE_LATENCY_ASC",
sortType: SORT_TYPE_LATENCY_ASC,
input: append(SvcSpans{}, baseSvcSpans...),
want: SvcSpans{
&SpanData{
Span: test.GenerateSpanWithDuration(t, "50ns", 50*time.Nanosecond),
},
&SpanData{
Span: test.GenerateSpanWithDuration(t, "75µs", 50*time.Microsecond),
},
&SpanData{
Span: test.GenerateSpanWithDuration(t, "100ms", 100*time.Millisecond),
},
&SpanData{
Span: test.GenerateSpanWithDuration(t, "101ms", 101*time.Millisecond),
},
&SpanData{
Span: test.GenerateSpanWithDuration(t, "230ms", 230*time.Millisecond),
},
},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
sortSvcSpans(tt.input, tt.sortType)
assert.Equal(t, tt.want, tt.input)
})
}
}
11 changes: 8 additions & 3 deletions tuiexporter/internal/telemetry/store.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ type Store struct {
filterSvc string
filterMetric string
filterLog string
sortTrace SortType
svcspans SvcSpans
svcspansFiltered SvcSpans
tracecache *TraceCache
Expand Down Expand Up @@ -145,13 +146,15 @@ func (s *Store) UpdatedAt() time.Time {
return s.updatedAt
}

// ApplyFilterTraces applies a filter to the traces
func (s *Store) ApplyFilterTraces(svc string) {
// ApplyFilterTraces applies a filter and sort to the traces
func (s *Store) ApplyFilterTraces(svc string, sortType SortType) {
s.filterSvc = svc
s.sortTrace = sortType
s.svcspansFiltered = []*SpanData{}

if svc == "" {
s.svcspansFiltered = s.svcspans
sortSvcSpans(s.svcspansFiltered, sortType)
return
}

Expand All @@ -165,10 +168,12 @@ func (s *Store) ApplyFilterTraces(svc string) {
s.svcspansFiltered = append(s.svcspansFiltered, span)
}
}

sortSvcSpans(s.svcspansFiltered, sortType)
}

func (s *Store) updateFilterService() {
s.ApplyFilterTraces(s.filterSvc)
s.ApplyFilterTraces(s.filterSvc, s.sortTrace)
}

// ApplyFilterMetrics applies a filter to the metrics
Expand Down
4 changes: 2 additions & 2 deletions tuiexporter/internal/telemetry/store_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ func TestStoreSpanFilters(t *testing.T) {
traceID := testdata.Spans[0].TraceID().String()
store.AddSpan(&payload)

store.ApplyFilterTraces("0-0")
store.ApplyFilterTraces("0-0", SORT_TYPE_NONE)
assert.Equal(t, 2, len(store.svcspansFiltered))
assert.Equal(t, traceID, store.GetTraceIDByFilteredIdx(0))
assert.Equal(t, traceID, store.GetTraceIDByFilteredIdx(1))
Expand All @@ -78,7 +78,7 @@ func TestStoreSpanFilters(t *testing.T) {
assert.Equal(t, "span-0-0-1", store.GetFilteredServiceSpansByIdx(0)[1].Span.Name())
// spans in test-service-2
assert.Equal(t, "span-1-0-0", store.GetFilteredServiceSpansByIdx(1)[0].Span.Name())
store.ApplyFilterTraces("service-2")
store.ApplyFilterTraces("service-2", SORT_TYPE_NONE)
assert.Equal(t, 1, len(store.svcspansFiltered))
assert.Equal(t, traceID, store.GetTraceIDByFilteredIdx(0))
assert.Equal(t, "", store.GetTraceIDByFilteredIdx(1))
Expand Down
13 changes: 13 additions & 0 deletions tuiexporter/internal/test/tracegen.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,3 +115,16 @@ func fillSpan(t *testing.T, span ptrace.Span, traceID, resourceIndex, scopeIndex
status.SetCode(ptrace.StatusCodeOk)
status.SetMessage("status ok")
}

// GenerateSpanWithDuration returns a span with specified span name and duration.
func GenerateSpanWithDuration(t *testing.T, spanName string, duration time.Duration) *ptrace.Span {
t.Helper()

span := ptrace.NewSpan()
span.SetName(spanName)
endTimeStamp := pcommon.NewTimestampFromTime(spanStartTimestamp.AsTime().Add(duration))
span.SetStartTimestamp(spanStartTimestamp)
span.SetEndTimestamp(endTimeStamp)

return &span
}
3 changes: 2 additions & 1 deletion tuiexporter/internal/tui/component/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ func NewLogDataForTable(logs *[]*telemetry.LogData) LogDataForTable {
// see: https://github.com/rivo/tview/wiki/VirtualTable
func (l LogDataForTable) GetCell(row, column int) *tview.TableCell {
if row == 0 {
return getHeaderCell(logTableHeader[:], column)
sortType := telemetry.SORT_TYPE_NONE
return getHeaderCell(logTableHeader[:], column, &sortType)
}
if row > 0 && row <= len(*l.logs) {
return getCellFromLog((*l.logs)[row-1], column)
Expand Down
3 changes: 2 additions & 1 deletion tuiexporter/internal/tui/component/metric.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ func NewMetricDataForTable(metrics *[]*telemetry.MetricData) MetricDataForTable
// see: https://github.com/rivo/tview/wiki/VirtualTable
func (m MetricDataForTable) GetCell(row, column int) *tview.TableCell {
if row == 0 {
return getHeaderCell(metricTableHeader[:], column)
sortType := telemetry.SORT_TYPE_NONE
return getHeaderCell(metricTableHeader[:], column, &sortType)
}
if row > 0 && row <= len(*m.metrics) {
return getCellFromMetrics((*m.metrics)[row-1], column)
Expand Down
Loading
Loading