Merge Dashboards & Dev Tools; WaterfallSpan; port to public ChartSpec

This commit is contained in:
ZacharyZhang-NY committed 2026-10-05 15:46:01 -04:00
1 parent 2a70035674
commit 408aa3ba7d
3 files changed
+16 -16

No files matched your search

+5 -5
View File
@@ -14,8 +14,8 @@ import (
"mujicaui/theme" "mujicaui/theme"
) )
// TraceSpan is one timed operation of a request; Parent is "" for the root. // WaterfallSpan is one timed operation of a request; Parent is "" for the root.
type TraceSpan struct { type WaterfallSpan struct {
ID, Parent string ID, Parent string
Name string Name string
Service string Service string
@@ -45,7 +45,7 @@ type traceState struct {
} }
// TraceWaterfall lays spans out by parent and time; choosing a span shows its detail. // TraceWaterfall lays spans out by parent and time; choosing a span shows its detail.
func TraceWaterfall(c *ui.Context, spans []TraceSpan, opts TraceWaterfallOptions) ChoiceResult { func TraceWaterfall(c *ui.Context, spans []WaterfallSpan, opts TraceWaterfallOptions) ChoiceResult {
if opts.Height < 0 { if opts.Height < 0 {
panic("mujica: TraceWaterfall Height is negative") panic("mujica: TraceWaterfall Height is negative")
} }
@@ -89,7 +89,7 @@ func TraceWaterfall(c *ui.Context, spans []TraceSpan, opts TraceWaterfallOptions
} }
// traceLayout orders spans depth-first by start, only when the spans change. // traceLayout orders spans depth-first by start, only when the spans change.
func traceLayout(st *traceState, spans []TraceSpan) { func traceLayout(st *traceState, spans []WaterfallSpan) {
key := fmt.Sprintf("%d", len(spans)) key := fmt.Sprintf("%d", len(spans))
if len(spans) > 0 { if len(spans) > 0 {
key += fmt.Sprintf("%p", &spans[0]) key += fmt.Sprintf("%p", &spans[0])
@@ -135,7 +135,7 @@ func traceLayout(st *traceState, spans []TraceSpan) {
} }
// traceSpanRow shows a span's name indented by depth beside its bar; it reports a choice. // traceSpanRow shows a span's name indented by depth beside its bar; it reports a choice.
func traceSpanRow(c *ui.Context, s TraceSpan, depth int, total time.Duration, slot int, selected bool) bool { func traceSpanRow(c *ui.Context, s WaterfallSpan, depth int, total time.Duration, slot int, selected bool) bool {
k := Tokens(c) k := Tokens(c)
d := Density(c) d := Density(c)
u := d.Unit() u := d.Unit()
+6 -6
View File
@@ -8,14 +8,14 @@ import (
"github.com/egoist/mygo/ui" "github.com/egoist/mygo/ui"
) )
var traceSample = []TraceSpan{ var traceSample = []WaterfallSpan{
{ID: "db", Parent: "api", Name: "SELECT guests", Service: "postgres", Start: 40 * time.Millisecond, Duration: 90 * time.Millisecond}, {ID: "db", Parent: "api", Name: "SELECT guests", Service: "postgres", Start: 40 * time.Millisecond, Duration: 90 * time.Millisecond},
{ID: "root", Name: "GET /ball", Service: "gateway", Duration: 320 * time.Millisecond}, {ID: "root", Name: "GET /ball", Service: "gateway", Duration: 320 * time.Millisecond},
{ID: "api", Parent: "root", Name: "ListGuests", Service: "api", Start: 20 * time.Millisecond, Duration: 200 * time.Millisecond}, {ID: "api", Parent: "root", Name: "ListGuests", Service: "api", Start: 20 * time.Millisecond, Duration: 200 * time.Millisecond},
{ID: "pay", Parent: "root", Name: "Charge", Service: "payments", Start: 230 * time.Millisecond, Duration: 80 * time.Millisecond, Error: true}, {ID: "pay", Parent: "root", Name: "Charge", Service: "payments", Start: 230 * time.Millisecond, Duration: 80 * time.Millisecond, Error: true},
} }
func traceTester(spans []TraceSpan, changes *int) *ui.Tester { func traceTester(spans []WaterfallSpan, changes *int) *ui.Tester {
return ui.NewTester(func(c *ui.Context) { return ui.NewTester(func(c *ui.Context) {
Use(c, Settings{}) Use(c, Settings{})
if TraceWaterfall(c, spans, TraceWaterfallOptions{}).Changed() { if TraceWaterfall(c, spans, TraceWaterfallOptions{}).Changed() {
@@ -54,10 +54,10 @@ func TestTraceWaterfallRulesAndMany(t *testing.T) {
if !tt.HasText("No spans") { if !tt.HasText("No spans") {
t.Fatal("empty") t.Fatal("empty")
} }
many := make([]TraceSpan, 100000) many := make([]WaterfallSpan, 100000)
many[0] = TraceSpan{ID: "0", Name: "root", Duration: time.Second} many[0] = WaterfallSpan{ID: "0", Name: "root", Duration: time.Second}
for i := 1; i < len(many); i++ { for i := 1; i < len(many); i++ {
many[i] = TraceSpan{ID: fmt.Sprint(i), Parent: "0", Name: fmt.Sprint("span ", i), Start: time.Duration(i) * time.Microsecond, Duration: time.Microsecond} many[i] = WaterfallSpan{ID: fmt.Sprint(i), Parent: "0", Name: fmt.Sprint("span ", i), Start: time.Duration(i) * time.Microsecond, Duration: time.Microsecond}
} }
tt = traceTester(many, &n) tt = traceTester(many, &n)
if got := len(tt.Texts()); got > 400 { if got := len(tt.Texts()); got > 400 {
@@ -68,7 +68,7 @@ func TestTraceWaterfallRulesAndMany(t *testing.T) {
t.Fatal("cycle did not panic") t.Fatal("cycle did not panic")
} }
}() }()
traceTester([]TraceSpan{{ID: "a", Parent: "b", Name: "a"}, {ID: "b", Parent: "a", Name: "b"}}, &n) traceTester([]WaterfallSpan{{ID: "a", Parent: "b", Name: "a"}, {ID: "b", Parent: "a", Name: "b"}}, &n)
} }
func TestTraceWaterfallSnapshot(t *testing.T) { func TestTraceWaterfallSnapshot(t *testing.T) {
+5 -5
View File
@@ -60,9 +60,9 @@ func UptimeBar(c *ui.Context, days []UptimeDay, opts UptimeBarOptions) ChartResu
return 0 return 0
} }
words := [...]string{msg(c, "uptime_bar.ok"), msg(c, "uptime_bar.degraded"), msg(c, "uptime_bar.down"), msg(c, "uptime_bar.nodata")} words := [...]string{msg(c, "uptime_bar.ok"), msg(c, "uptime_bar.degraded"), msg(c, "uptime_bar.down"), msg(c, "uptime_bar.nodata")}
res, _ := ch.build(chartSpec{ res, _ := ch.build(ChartSpec{
n: n, Marks: n,
draw: func(p *ui.Painter, r ui.Rect, active int) { Draw: func(p *ui.Painter, r ui.Rect, active int) {
b := band(r) b := band(r)
for i, d := range days { for i, d := range days {
x, w := internal.Snap(p, b.Start(i)), max(b.Width(), 1) x, w := internal.Snap(p, b.Start(i)), max(b.Width(), 1)
@@ -76,11 +76,11 @@ func UptimeBar(c *ui.Context, days []UptimeDay, opts UptimeBarOptions) ChartResu
} }
} }
}, },
pick: func(r ui.Rect, x, _ float32) int { Pick: func(r ui.Rect, x, _ float32) int {
b := band(r) b := band(r)
return max(0, min(n-1, int((x-r.X)/b.Step()))) return max(0, min(n-1, int((x-r.X)/b.Step())))
}, },
readout: func(i int) string { Readout: func(i int) string {
d := days[i] d := days[i]
s := fmt.Sprintf("%s · %s", d.Date.String(), words[level(d.Uptime)]) s := fmt.Sprintf("%s · %s", d.Date.String(), words[level(d.Uptime)])
if !math.IsNaN(d.Uptime) { if !math.IsNaN(d.Uptime) {