Skip to content

Commit

Permalink
Merge pull request #174 from ymtdzzz/feature/full_text_modal_for_attr…
Browse files Browse the repository at this point in the history
…ibutes

Full text modal for attribute tree
  • Loading branch information
ymtdzzz authored Nov 4, 2024
2 parents 93fe71c + 8b3cdc7 commit ad9d851
Show file tree
Hide file tree
Showing 8 changed files with 120 additions and 19 deletions.
4 changes: 3 additions & 1 deletion tuiexporter/internal/tui/component/log.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ func getCellFromLog(log *telemetry.LogData, column int) *tview.TableCell {
return tview.NewTableCell(text)
}

func getLogInfoTree(commands *tview.TextView, l *telemetry.LogData, tcache *telemetry.TraceCache, drawTimelineFn func(traceID string)) *tview.TreeView {
func getLogInfoTree(commands *tview.TextView, showModalFn showModalFunc, hideModalFn hideModalFunc, l *telemetry.LogData, tcache *telemetry.TraceCache, drawTimelineFn func(traceID string)) *tview.TreeView {
if l == nil {
return tview.NewTreeView()
}
Expand Down Expand Up @@ -169,6 +169,8 @@ func getLogInfoTree(commands *tview.TextView, l *telemetry.LogData, tcache *tele
node.SetExpanded(!node.IsExpanded())
})

attachModalForTreeAttributes(tree, showModalFn, hideModalFn)

registerCommandList(commands, tree, nil, KeyMaps{
{
key: tcell.NewEventKey(tcell.KeyRune, 'L', tcell.ModCtrl),
Expand Down
2 changes: 1 addition & 1 deletion tuiexporter/internal/tui/component/log_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,7 @@ func TestGetLogInfoTree(t *testing.T) {
screen.Init()
screen.SetSize(sw, sh)

gottree := getLogInfoTree(nil, logs[0], nil, nil)
gottree := getLogInfoTree(nil, noopShowModalFn, noopHideModalFn, logs[0], nil, nil)
gottree.SetRect(0, 0, sw, sh)
gottree.Draw(screen)
screen.Sync()
Expand Down
4 changes: 3 additions & 1 deletion tuiexporter/internal/tui/component/metric.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func getCellFromMetrics(metric *telemetry.MetricData, column int) *tview.TableCe
return tview.NewTableCell(text)
}

func getMetricInfoTree(commands *tview.TextView, m *telemetry.MetricData) *tview.TreeView {
func getMetricInfoTree(commands *tview.TextView, showModalFn showModalFunc, hideModalFn hideModalFunc, m *telemetry.MetricData) *tview.TreeView {
if m == nil {
return nil
}
Expand Down Expand Up @@ -371,6 +371,8 @@ func getMetricInfoTree(commands *tview.TextView, m *telemetry.MetricData) *tview
node.SetExpanded(!node.IsExpanded())
})

attachModalForTreeAttributes(tree, showModalFn, hideModalFn)

registerCommandList(commands, tree, nil, KeyMaps{
{
key: tcell.NewEventKey(tcell.KeyRune, 'L', tcell.ModCtrl),
Expand Down
49 changes: 46 additions & 3 deletions tuiexporter/internal/tui/component/page.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package component

import (
"fmt"
"log"
"strings"

Expand All @@ -16,6 +17,7 @@ const (
PAGE_LOGS = "Logs"
PAGE_DEBUG_LOG = "DebugLog"
PAGE_METRICS = "Metrics"
PAGE_MODAL = "Modal"

DEFAULT_PROPORTION_TRACE_DETAILS = 20
DEFAULT_PROPORTION_TRACE_TABLE = 30
Expand All @@ -32,6 +34,7 @@ type TUIPages struct {
metrics *tview.Flex
logs *tview.Flex
debuglog *tview.Flex
modal *tview.Flex
current string
setFocusFn func(p tview.Primitive)
// This is used when other components trigger to draw the timeline
Expand Down Expand Up @@ -71,6 +74,20 @@ func (p *TUIPages) ToggleLog() {
}
}

func (p *TUIPages) showModal(current tview.Primitive, text string) *tview.TextView {
textView := p.updateModelPage(text)
p.pages.ShowPage(PAGE_MODAL)
p.pages.SendToFront(PAGE_MODAL)
p.setFocusFn(current)
return textView
}

func (p *TUIPages) hideModal(current tview.Primitive) {
p.pages.SendToBack(PAGE_MODAL)
p.pages.HidePage(PAGE_MODAL)
p.setFocusFn(current)
}

// TogglePage toggles Traces & Logs page.
func (p *TUIPages) TogglePage() {
if p.current == PAGE_TRACES {
Expand All @@ -88,6 +105,10 @@ func (p *TUIPages) switchToPage(name string) {
}

func (p *TUIPages) registerPages(store *telemetry.Store) {
modal, _ := p.createModalPage("")
p.modal = modal
p.pages.AddPage(PAGE_MODAL, modal, true, true)

logpage := p.createDebugLogPage()
p.debuglog = logpage
p.pages.AddPage(PAGE_DEBUG_LOG, logpage, true, true)
Expand Down Expand Up @@ -230,7 +251,7 @@ func (p *TUIPages) createTracePage(store *telemetry.Store) *tview.Flex {
return
}
details.Clear()
details.AddItem(getTraceInfoTree(commands, store.GetFilteredServiceSpansByIdx(row-1)), 0, 1, true)
details.AddItem(getTraceInfoTree(commands, p.showModal, p.hideModal, store.GetFilteredServiceSpansByIdx(row-1)), 0, 1, true)
log.Printf("selected row(original): %d", row)
})
tableContainer.
Expand Down Expand Up @@ -284,6 +305,26 @@ func (p *TUIPages) createTimelinePage() *tview.Flex {
return page
}

func (p *TUIPages) createModalPage(text string) (*tview.Flex, *tview.TextView) {
textView := tview.NewTextView()
textView.SetBorder(true)
fmt.Fprint(textView, text)
return tview.NewFlex().SetDirection(tview.FlexColumn).
AddItem(nil, 0, 2, false).
AddItem(nil, 0, 2, false).
AddItem(tview.NewFlex().SetDirection(tview.FlexRow).
AddItem(nil, 0, 2, false).
AddItem(nil, 0, 1, false).
AddItem(textView, 0, 1, false), 0, 3, false), textView
}

func (p *TUIPages) updateModelPage(text string) *tview.TextView {
modal, textView := p.createModalPage(text)
p.modal = modal
p.pages.AddPage(PAGE_MODAL, modal, true, false)
return textView
}

func (p *TUIPages) showTimelineByRow(store *telemetry.Store, row int) {
if store == nil {
return
Expand All @@ -302,6 +343,8 @@ func (p *TUIPages) showTimeline(traceID string, tcache *telemetry.TraceCache, lc
timeline := tview.NewFlex().SetDirection(tview.FlexRow)
tl := DrawTimeline(
p.commandsTimeline,
p.showModal,
p.hideModal,
traceID,
tcache,
lcache,
Expand Down Expand Up @@ -424,7 +467,7 @@ func (p *TUIPages) createMetricsPage(store *telemetry.Store) *tview.Flex {
}
selected := store.GetFilteredMetricByIdx(row - 1)
details.Clear()
details.AddItem(getMetricInfoTree(commands, selected), 0, 1, true)
details.AddItem(getMetricInfoTree(commands, p.showModal, p.hideModal, selected), 0, 1, true)
// TODO: async rendering with spinner
chart.Clear()
chart.AddItem(drawMetricChartByRow(commands, store, row-1), 0, 1, true)
Expand Down Expand Up @@ -580,7 +623,7 @@ func (p *TUIPages) createLogPage(store *telemetry.Store) *tview.Flex {
}
selected := store.GetFilteredLogByIdx(row - 1)
details.Clear()
details.AddItem(getLogInfoTree(commands, selected, store.GetTraceCache(), func(traceID string) {
details.AddItem(getLogInfoTree(commands, p.showModal, p.hideModal, selected, store.GetTraceCache(), func(traceID string) {
p.showTimeline(traceID, store.GetTraceCache(), store.GetLogCache(), func(pr tview.Primitive) {
p.setFocusFn(pr)
})
Expand Down
14 changes: 8 additions & 6 deletions tuiexporter/internal/tui/component/timeline.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ type spanTreeNode struct {
children []*spanTreeNode
}

func DrawTimeline(commands *tview.TextView, traceID string, tcache *telemetry.TraceCache, lcache *telemetry.LogCache, setFocusFn func(p tview.Primitive)) tview.Primitive {
func DrawTimeline(commands *tview.TextView, showModalFn showModalFunc, hideModalFn hideModalFunc, traceID string, tcache *telemetry.TraceCache, lcache *telemetry.LogCache, setFocusFn func(p tview.Primitive)) tview.Primitive {
if traceID == "" || tcache == nil {
return newTextView(commands, "No spans found")
}
Expand Down Expand Up @@ -122,7 +122,7 @@ func DrawTimeline(commands *tview.TextView, traceID string, tcache *telemetry.Tr
}

// details
details := getSpanInfoTree(commands, nodes[0].span, TIMELINE_TREE_TITLE)
details := getSpanInfoTree(commands, showModalFn, hideModalFn, nodes[0].span, TIMELINE_TREE_TITLE)
detailspro := DEFAULT_PROPORTION_TIMELINE_DETAILS
gridpro := DEFAULT_PROPORTION_TIMELINE_GRID

Expand Down Expand Up @@ -154,7 +154,7 @@ func DrawTimeline(commands *tview.TextView, traceID string, tcache *telemetry.Tr
// update details
oldDetails := traceContainer.GetItem(TIMELINE_DETAILS_IDX)
traceContainer.RemoveItem(oldDetails)
details := getSpanInfoTree(commands, nodes[currentRow].span, TIMELINE_TREE_TITLE)
details := getSpanInfoTree(commands, showModalFn, hideModalFn, nodes[currentRow].span, TIMELINE_TREE_TITLE)
details.SetInputCapture(detailsInputFunc(traceContainer, grid, details, &gridpro, &detailspro))
traceContainer.AddItem(details, 0, detailspro, false)
}
Expand All @@ -163,12 +163,12 @@ func DrawTimeline(commands *tview.TextView, traceID string, tcache *telemetry.Tr
if currentRow > 0 {
currentRow--
setFocusFn(tvs[currentRow])
details = getSpanInfoTree(commands, nodes[currentRow].span, TIMELINE_TREE_TITLE)
details = getSpanInfoTree(commands, showModalFn, hideModalFn, nodes[currentRow].span, TIMELINE_TREE_TITLE)

// update details
oldDetails := traceContainer.GetItem(TIMELINE_DETAILS_IDX)
traceContainer.RemoveItem(oldDetails)
details := getSpanInfoTree(commands, nodes[currentRow].span, TIMELINE_TREE_TITLE)
details := getSpanInfoTree(commands, showModalFn, hideModalFn, nodes[currentRow].span, TIMELINE_TREE_TITLE)
details.SetInputCapture(detailsInputFunc(traceContainer, grid, details, &gridpro, &detailspro))
traceContainer.AddItem(details, 0, detailspro, false)
}
Expand Down Expand Up @@ -465,7 +465,7 @@ func createSpan(color tcell.Color, total, start, end time.Duration) (span *tview
})
}

func getSpanInfoTree(commands *tview.TextView, span *telemetry.SpanData, title string) *tview.TreeView {
func getSpanInfoTree(commands *tview.TextView, showModalFn showModalFunc, hideModalFn hideModalFunc, span *telemetry.SpanData, title string) *tview.TreeView {
traceID := span.Span.TraceID().String()
sname, _ := span.ResourceSpan.Resource().Attributes().Get("service.name")
root := tview.NewTreeNode(fmt.Sprintf("%s (%s)", sname.AsString(), traceID))
Expand Down Expand Up @@ -592,6 +592,8 @@ func getSpanInfoTree(commands *tview.TextView, span *telemetry.SpanData, title s
node.SetExpanded(!node.IsExpanded())
})

attachModalForTreeAttributes(tree, showModalFn, hideModalFn)

registerCommandList(commands, tree, nil, KeyMaps{
{
key: tcell.NewEventKey(tcell.KeyRune, 'L', tcell.ModCtrl),
Expand Down
8 changes: 3 additions & 5 deletions tuiexporter/internal/tui/component/trace.go
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ func getHeaderCell(header []string, column int, sortType *telemetry.SortType) *t
return cell
}

func getTraceInfoTree(commands *tview.TextView, spans []*telemetry.SpanData) *tview.TreeView {
func getTraceInfoTree(commands *tview.TextView, showModalFn showModalFunc, hideModalFn hideModalFunc, spans []*telemetry.SpanData) *tview.TreeView {
if len(spans) == 0 {
return tview.NewTreeView()
}
Expand Down Expand Up @@ -157,9 +157,7 @@ func getTraceInfoTree(commands *tview.TextView, spans []*telemetry.SpanData) *tv

root.AddChild(resource)

tree.SetSelectedFunc(func(node *tview.TreeNode) {
node.SetExpanded(!node.IsExpanded())
})
attachModalForTreeAttributes(tree, showModalFn, hideModalFn)

registerCommandList(commands, tree, nil, KeyMaps{
{
Expand All @@ -172,7 +170,7 @@ func getTraceInfoTree(commands *tview.TextView, spans []*telemetry.SpanData) *tv
},
{
key: tcell.NewEventKey(tcell.KeyEnter, ' ', tcell.ModNone),
description: "Toggle folding the child nodes",
description: "Toggle folding (parent), Show full text (child)",
},
})

Expand Down
11 changes: 9 additions & 2 deletions tuiexporter/internal/tui/component/trace_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,19 @@ import (
"time"

"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
"github.com/stretchr/testify/assert"
"github.com/ymtdzzz/otel-tui/tuiexporter/internal/telemetry"
"github.com/ymtdzzz/otel-tui/tuiexporter/internal/test"
"go.opentelemetry.io/collector/pdata/ptrace"
)

var noopShowModalFn showModalFunc = func(p tview.Primitive, s string) *tview.TextView {
return tview.NewTextView()
}

var noopHideModalFn hideModalFunc = func(p tview.Primitive) {}

func TestSpanDataForTable(t *testing.T) {
// traceid: 1
// └- resource: test-service-1
Expand Down Expand Up @@ -232,7 +239,7 @@ func TestGetTraceInfoTree(t *testing.T) {
screen.Init()
screen.SetSize(sw, sh)

gottree := getTraceInfoTree(nil, spans)
gottree := getTraceInfoTree(nil, noopShowModalFn, noopHideModalFn, spans)
gottree.SetRect(0, 0, sw, sh)
gottree.Draw(screen)
screen.Sync()
Expand Down Expand Up @@ -276,5 +283,5 @@ func TestGetTraceInfoTree(t *testing.T) {
}

func TestGetTraceInfoTreeNoSpans(t *testing.T) {
assert.Nil(t, getTraceInfoTree(nil, nil).GetRoot())
assert.Nil(t, getTraceInfoTree(nil, noopShowModalFn, noopHideModalFn, nil).GetRoot())
}
47 changes: 47 additions & 0 deletions tuiexporter/internal/tui/component/tree.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package component

import (
"github.com/gdamore/tcell/v2"
"github.com/rivo/tview"
)

type showModalFunc func(tview.Primitive, string) *tview.TextView

type hideModalFunc func(tview.Primitive)

func attachModalForTreeAttributes(tree *tview.TreeView, showFn showModalFunc, hideFn hideModalFunc) {
var currentModalNode *tview.TreeNode = nil
tree.SetSelectedFunc(func(node *tview.TreeNode) {
if len(node.GetChildren()) > 0 {
node.SetExpanded(!node.IsExpanded())
return
}
if currentModalNode == node {
hideFn(tree)
currentModalNode = nil
return
}
textView := showFn(tree, node.GetText())
textView.SetTitle("Scroll (Ctrl+J, Ctrl+K)")
currentModalNode = node
tree.SetInputCapture(func(event *tcell.EventKey) *tcell.EventKey {
switch event.Key() {
case tcell.KeyCtrlJ:
row, col := textView.GetScrollOffset()
textView.ScrollTo(row+1, col)
return nil
case tcell.KeyCtrlK:
row, col := textView.GetScrollOffset()
textView.ScrollTo(row-1, col)
return nil
}
return event
})
})
tree.SetChangedFunc(func(node *tview.TreeNode) {
if currentModalNode != nil {
hideFn(tree)
currentModalNode = nil
}
})
}

0 comments on commit ad9d851

Please sign in to comment.