[Feature] beautify watch command

This commit is contained in:
hengyoush
2024-10-12 04:28:41 +08:00
parent 70c53b0dfe
commit 97459fb376
12 changed files with 106 additions and 49 deletions

View File

@@ -112,7 +112,7 @@ func SetupAgent(options ac.AgentOptions) {
time.Sleep(time.Second * 1)
}
} else {
watch.RunWatchRender(ctx, recordsChannel)
watch.RunWatchRender(ctx, recordsChannel, options.WatchOptions)
}
common.AgentLog.Infoln("Kyanos Stopped")
return

View File

@@ -9,6 +9,7 @@ import (
"kyanos/agent/conn"
"kyanos/agent/metadata"
"kyanos/agent/protocol"
"kyanos/agent/render/watch"
"kyanos/bpf"
"kyanos/common"
"os"
@@ -45,6 +46,7 @@ type AgentOptions struct {
PerfEventBufferSizeForData int
PerfEventBufferSizeForEvent int
DisableOpensslUprobe bool
WatchOptions watch.WatchOptions
DockerEndpoint string
ContainerdEndpoint string
@@ -115,5 +117,6 @@ func ValidateAndRepairOptions(options AgentOptions) AgentOptions {
if newOptions.CriRuntimeEndpoint != "" {
newOptions.CriRuntimeEndpoint = getEndpoint(newOptions.CriRuntimeEndpoint)
}
newOptions.WatchOptions.Init()
return newOptions
}

View File

@@ -0,0 +1,16 @@
package watch
import "strings"
type WatchOptions struct {
wideOutput bool
Opts string
}
func (w *WatchOptions) Init() {
if w.Opts != "" {
if strings.Contains(w.Opts, "wide") {
w.wideOutput = true
}
}
}

View File

@@ -8,6 +8,7 @@ import (
"kyanos/bpf"
c "kyanos/common"
"os"
"slices"
"strconv"
"strings"
"sync"
@@ -44,6 +45,7 @@ type model struct {
help help.Model
chosen bool
ready bool
wide bool
}
func (m model) Init() tea.Cmd { return tea.Batch(m.spinner.Tick) }
@@ -59,13 +61,28 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
records := (*m.records)[len(rows):]
idx := len(rows) + 1
for i, record := range records {
row := table.Row{
fmt.Sprintf("%d", i+idx),
record.ConnDesc.SimpleString(),
bpf.ProtocolNamesMap[bpf.AgentTrafficProtocolT(record.ConnDesc.Protocol)],
fmt.Sprintf("%.2f", c.ConvertDurationToMillisecondsIfNeeded(record.TotalDuration, false)),
fmt.Sprintf("%d", record.ReqSize),
fmt.Sprintf("%d", record.RespSize),
var row table.Row
if m.wide {
row = table.Row{
fmt.Sprintf("%d", i+idx),
c.GetPidCmdString(int32(record.Pid)),
record.ConnDesc.SimpleString(),
bpf.ProtocolNamesMap[bpf.AgentTrafficProtocolT(record.ConnDesc.Protocol)],
fmt.Sprintf("%.2f", c.ConvertDurationToMillisecondsIfNeeded(record.TotalDuration, false)),
fmt.Sprintf("%d", record.ReqSize),
fmt.Sprintf("%d", record.RespSize),
fmt.Sprintf("%.2f", c.ConvertDurationToMillisecondsIfNeeded(record.BlackBoxDuration, false)),
fmt.Sprintf("%.2f", c.ConvertDurationToMillisecondsIfNeeded(record.ReadFromSocketBufferDuration, false)),
}
} else {
row = table.Row{
fmt.Sprintf("%d", i+idx),
record.ConnDesc.SimpleString(),
bpf.ProtocolNamesMap[bpf.AgentTrafficProtocolT(record.ConnDesc.Protocol)],
fmt.Sprintf("%.2f", c.ConvertDurationToMillisecondsIfNeeded(record.TotalDuration, false)),
fmt.Sprintf("%d", record.ReqSize),
fmt.Sprintf("%d", record.RespSize),
}
}
rows = append(rows, row)
}
@@ -107,7 +124,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
r := (*m.records)[idx-1]
line := strings.Repeat("+", m.viewport.Width)
m.viewport.SetContent("[Request]\n\n" + c.TruncateString(r.Req.FormatToString(), 1024) + "\n" + line + "\n[Response]\n\n" +
c.TruncateString(r.Resp.FormatToString(), 10240) + "\n" + line)
c.TruncateString(r.Resp.FormatToString(), 10240))
} else {
panic("!")
}
@@ -215,14 +232,23 @@ func (k watchKeyMap) FullHelp() [][]key.Binding {
return [][]key.Binding{{detailViewKeyMap["n"], detailViewKeyMap["p"]}}
}
func RunWatchRender(ctx context.Context, ch chan *common.AnnotatedRecord) {
func RunWatchRender(ctx context.Context, ch chan *common.AnnotatedRecord, options WatchOptions) {
columns := []table.Column{
{Title: "id", Width: 3},
{Title: "Connection", Width: 40},
{Title: "Protocol", Width: 10},
{Title: "Total Time", Width: 10},
{Title: "Req Size", Width: 10},
{Title: "Resp Size", Width: 10},
{Title: "Proto", Width: 5},
{Title: "TotalTime", Width: 10},
{Title: "ReqSize", Width: 7},
{Title: "RespSize", Width: 8},
}
if options.wideOutput {
columns = slices.Insert(columns, 1, table.Column{
Title: "Proc", Width: 20,
})
columns = append(columns, []table.Column{
{Title: "Net/Internal", Width: 15},
{Title: "ReadSocket", Width: 12},
}...)
}
rows := []table.Row{}
t := table.New(
@@ -230,7 +256,7 @@ func RunWatchRender(ctx context.Context, ch chan *common.AnnotatedRecord) {
table.WithRows(rows),
table.WithFocused(true),
table.WithHeight(7),
table.WithWidth(96),
// table.WithWidth(96),
)
s := table.DefaultStyles()
s.Header = s.Header.
@@ -244,7 +270,7 @@ func RunWatchRender(ctx context.Context, ch chan *common.AnnotatedRecord) {
Bold(false)
t.SetStyles(s)
records := &[]*common.AnnotatedRecord{}
m := model{t, viewport.New(100, 100), records, spinner.New(spinner.WithSpinner(spinner.Dot)), help.NewModel(), false, false}
m := model{t, viewport.New(100, 100), records, spinner.New(spinner.WithSpinner(spinner.Dot)), help.New(), false, false, options.wideOutput}
go func(mod *model, channel chan *common.AnnotatedRecord) {
for {
select {

View File

@@ -42,7 +42,9 @@ func ParseSide(side string) (common.SideEnum, error) {
}
}
func startAgent(options ac.AgentOptions) {
var options ac.AgentOptions
func startAgent() {
side, err := ParseSide(SidePar)
if err != nil {
return

View File

@@ -1,7 +1,6 @@
package cmd
import (
ac "kyanos/agent/common"
"kyanos/agent/protocol"
"github.com/spf13/cobra"
@@ -23,15 +22,14 @@ var httpCmd *cobra.Command = &cobra.Command{
if err != nil {
logger.Fatalf("invalid host: %v\n", err)
}
startAgent(ac.AgentOptions{
MessageFilter: protocol.HttpFilter{
TargetPath: path,
TargetMethods: methods,
TargetHostName: host,
},
LatencyFilter: initLatencyFilter(cmd),
SizeFilter: initSizeFilter(cmd),
})
options.MessageFilter = protocol.HttpFilter{
TargetPath: path,
TargetMethods: methods,
TargetHostName: host,
}
options.LatencyFilter = initLatencyFilter(cmd)
options.SizeFilter = initSizeFilter(cmd)
startAgent()
},
}

View File

@@ -1,7 +1,6 @@
package cmd
import (
ac "kyanos/agent/common"
"kyanos/agent/protocol/mysql"
"github.com/spf13/cobra"
@@ -11,11 +10,10 @@ var mysqlCmd *cobra.Command = &cobra.Command{
Use: "mysql",
Short: "watch MYSQL message",
Run: func(cmd *cobra.Command, args []string) {
startAgent(ac.AgentOptions{
MessageFilter: mysql.MysqlFilter{},
LatencyFilter: initLatencyFilter(cmd),
SizeFilter: initSizeFilter(cmd),
})
options.MessageFilter = mysql.MysqlFilter{}
options.LatencyFilter = initLatencyFilter(cmd)
options.SizeFilter = initSizeFilter(cmd)
startAgent()
},
}

View File

@@ -1,7 +1,6 @@
package cmd
import (
ac "kyanos/agent/common"
"kyanos/agent/protocol"
"github.com/spf13/cobra"
@@ -23,15 +22,15 @@ var redisCmd *cobra.Command = &cobra.Command{
if err != nil {
logger.Fatalf("invalid prefix: %v\n", err)
}
startAgent(ac.AgentOptions{
MessageFilter: protocol.RedisFilter{
TargetCommands: commands,
TargetKeys: keys,
KeyPrefix: prefix,
},
LatencyFilter: initLatencyFilter(cmd),
SizeFilter: initSizeFilter(cmd),
})
options.MessageFilter = protocol.RedisFilter{
TargetCommands: commands,
TargetKeys: keys,
KeyPrefix: prefix,
}
options.LatencyFilter = initLatencyFilter(cmd)
options.SizeFilter = initSizeFilter(cmd)
startAgent()
},
}

View File

@@ -2,7 +2,6 @@ package cmd
import (
"fmt"
ac "kyanos/agent/common"
"kyanos/agent/metadata/k8s"
"kyanos/common"
"strings"
@@ -34,7 +33,7 @@ sudo kyanos stat http --metrics t --group-by remote-ip
sudo kyanos stat http --metrics t --samples 3 --full-body
sudo kyanos stat http --metrics tq --sort-by avg --group-by remote-ip`,
Run: func(cmd *cobra.Command, args []string) {
startAgent(ac.AgentOptions{})
startAgent()
},
}

View File

@@ -4,7 +4,6 @@ import (
"fmt"
"kyanos/agent/analysis"
anc "kyanos/agent/analysis/common"
ac "kyanos/agent/common"
"slices"
"github.com/spf13/cobra"
@@ -40,7 +39,9 @@ sudo kyanos stat http --metrics tqp
`,
PersistentPreRun: func(cmd *cobra.Command, args []string) { Mode = AnalysisMode },
Run: func(cmd *cobra.Command, args []string) {
startAgent(ac.AgentOptions{LatencyFilter: initLatencyFilter(cmd), SizeFilter: initSizeFilter(cmd)})
options.LatencyFilter = initLatencyFilter(cmd)
options.SizeFilter = initSizeFilter(cmd)
startAgent()
},
}
var enabledMetricsString string

View File

@@ -2,7 +2,6 @@ package cmd
import (
"fmt"
ac "kyanos/agent/common"
"github.com/spf13/cobra"
)
@@ -25,7 +24,9 @@ sudo kyanos watch mysql --latency 100 --req-size 1024 --resp-size 2048
if list {
fmt.Println([]string{"http", "redis", "mysql"})
} else {
startAgent(ac.AgentOptions{LatencyFilter: initLatencyFilter(cmd), SizeFilter: initSizeFilter(cmd)})
options.LatencyFilter = initLatencyFilter(cmd)
options.SizeFilter = initSizeFilter(cmd)
startAgent()
}
}
},
@@ -36,6 +37,7 @@ func init() {
watchCmd.PersistentFlags().Float64("latency", 0, "Filter based on request response time")
watchCmd.PersistentFlags().Int64("req-size", 0, "Filter based on request bytes size")
watchCmd.PersistentFlags().Int64("resp-size", 0, "Filter based on response bytes size")
watchCmd.PersistentFlags().StringVarP(&options.WatchOptions.Opts, "output", "o", "", "Can be `wide`")
watchCmd.PersistentFlags().StringVar(&SidePar, "side", "all", "Filter based on connection side. can be: server | client")
watchCmd.Flags().SortFlags = false
watchCmd.PersistentFlags().SortFlags = false

View File

@@ -44,3 +44,16 @@ func ProcPidRootPath(pid int, paths ...string) string {
func GetAllPids() ([]int32, error) {
return process.Pids()
}
func GetPidCmdString(pid int32) string {
proc, err := process.NewProcess(pid)
if err != nil {
return fmt.Sprintf("%d<%s>", pid, "unknwon")
} else {
name, err := proc.Name()
if err != nil {
return fmt.Sprintf("%d<%s>", pid, "unknwon")
}
return fmt.Sprintf("%d<%s>", pid, name)
}
}