mirror of
https://github.com/hengyoush/kyanos.git
synced 2025-12-20 01:03:46 +08:00
[Feature] beautify watch command
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
16
agent/render/watch/option.go
Normal file
16
agent/render/watch/option.go
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
@@ -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
|
||||
|
||||
18
cmd/http.go
18
cmd/http.go
@@ -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()
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
10
cmd/mysql.go
10
cmd/mysql.go
@@ -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()
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
19
cmd/redis.go
19
cmd/redis.go
@@ -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()
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user