Skip to content

Commit

Permalink
[Improvement] Optimize cmdline output
Browse files Browse the repository at this point in the history
  • Loading branch information
hengyoush committed Sep 16, 2024
1 parent fc3c9f4 commit c4ff3fa
Show file tree
Hide file tree
Showing 12 changed files with 126 additions and 81 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# kyanos
![](docs/banner.png)

![GitHub Release](https://img.shields.io/badge/language-golang-blue) ![GitHub Release](https://img.shields.io/badge/os-linux-239120) [![GitHub last commit](https://img.shields.io/github/last-commit/hengyoush/kyanos)](#) [![GitHub release](https://img.shields.io/github/v/release/hengyoush/kyanos)](#) [![Free](https://img.shields.io/badge/free_for_non_commercial_use-brightgreen)](#-license)

⭐ 觉得kyanos还不错?点个star吧~
Expand Down
7 changes: 6 additions & 1 deletion agent/analysis/classfier.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,12 @@ func init() {
return ar.ConnDesc.String()
}
classIdHumanReadableMap[HttpPath] = func(ar *AnnotatedRecord) string {
return ar.Record.Request().(*protocol.ParsedHttpRequest).Path
httpReq, ok := ar.Record.Request().(*protocol.ParsedHttpRequest)
if !ok {
return "__not_a_http_req__"
} else {
return httpReq.Path
}
}

classIdHumanReadableMap[Protocol] = func(ar *AnnotatedRecord) string {
Expand Down
1 change: 0 additions & 1 deletion agent/compatible/type.go
Original file line number Diff line number Diff line change
Expand Up @@ -117,7 +117,6 @@ func init() {
SupportConstants: true,
SupportRawTracepoint: true,
SupportRingBuffer: true,
SupportXDP: false,
SupportBTF: true,
},
}
Expand Down
6 changes: 0 additions & 6 deletions cmd/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import (
"kyanos/agent"
"kyanos/agent/protocol"
"kyanos/common"
"net"

"github.com/jefurry/logrus"
"github.com/sevlyar/go-daemon"
Expand Down Expand Up @@ -52,11 +51,6 @@ func startAgent(options agent.AgentOptions) {
options.AnalysisOptions = analysisOptions
options.Side = side
}
_, err = net.InterfaceByName(IfName)
if err != nil {
logger.Errorf("Start Kyanos failed: %v", err)
return
}
options.IfName = IfName
options.BTFFilePath = BTFFilePath
options.BPFVerifyLogSize = BPFVerifyLogSize
Expand Down
6 changes: 3 additions & 3 deletions cmd/http.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ var httpCmd *cobra.Command = &cobra.Command{
}

func init() {
httpCmd.Flags().StringSlice("method", []string{}, "--method GET,POST")
httpCmd.Flags().String("host", "", "--host www.baidu.com")
httpCmd.Flags().String("path", "", "--path /foo/bar")
httpCmd.Flags().StringSlice("method", []string{}, "Specify the HTTP method to monitor(GET, POST), seperate by ','")
httpCmd.Flags().String("host", "", "Specify the HTTP host to monitor, like: 'ubuntu.com'")
httpCmd.Flags().String("path", "", "Specify the HTTP path to monitor, like: '/foo/bar'")
httpCmd.Flags().SortFlags = false
httpCmd.PersistentFlags().SortFlags = false
copy := *httpCmd
Expand Down
2 changes: 1 addition & 1 deletion cmd/mysql.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import (
)

var mysqlCmd *cobra.Command = &cobra.Command{
Use: "mysql ",
Use: "mysql",
Short: "watch MYSQL message",
Run: func(cmd *cobra.Command, args []string) {
startAgent(agent.AgentOptions{
Expand Down
6 changes: 3 additions & 3 deletions cmd/redis.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,9 @@ var redisCmd *cobra.Command = &cobra.Command{
}

func init() {
redisCmd.Flags().StringSlice("command", []string{}, "--command GET,SET")
redisCmd.Flags().StringSlice("keys", []string{}, "--keys key1,key2")
redisCmd.Flags().String("key-prefix", "", "--key-prefix foo:bar:")
redisCmd.Flags().StringSlice("command", []string{}, "Specify the redis command to monitor(GET, SET), seperate by ','")
redisCmd.Flags().StringSlice("keys", []string{}, "Specify the redis keys to monitor, seperate by ','")
redisCmd.Flags().String("key-prefix", "", "Specify the redis key prefix to monitor")
redisCmd.Flags().SortFlags = false
redisCmd.PersistentFlags().SortFlags = false
copy := *redisCmd
Expand Down
62 changes: 41 additions & 21 deletions cmd/root.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,18 +13,30 @@ import (
var logger *logrus.Logger = common.DefaultLog

var rootCmd = &cobra.Command{
Use: "kyanos <command> [<args>]",
Short: "Kyanos is a user-friendly, fast, non-intrusive command-line tool base on eBPF to find/analyze/diagnose network issues.",
Use: `kyanos <command> [flags]`,
Short: "Kyanos is a command-line tool for monitoring, troubleshooting, and analyzing network issues using eBPF. \n" +
"It helps you quickly diagnose network-related problems in applications," +
" such as slow queries, high traffic, and other anomalies.\n" +
"More info: https://github.com/hengyoush/kyanos",
CompletionOptions: cobra.CompletionOptions{
DisableDefaultCmd: true,
},
DisableFlagsInUseLine: true,
Example: `
sudo kyanos
sudo kyanos watch http --pid 1234 --path /foo/bar
sudo kyanos watch redis --comands GET,SET
sudo kyanos watch mysql --latency 100
sudo kyanos stat http --metrics t --interval 5
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(agent.AgentOptions{})
},
}

// var LogDir string
// var Verbose bool
var Daemon bool
var Debug bool
var FilterPid int64
Expand All @@ -44,27 +56,35 @@ var ConntrackLogLevel int32
var ProtocolLogLevel int32

func init() {
// rootCmd.PersistentFlags().StringVar(&LogDir, "log-dir", "", "log file dir")
// rootCmd.PersistentFlags().BoolVar(&Daemon, "daemon", false, "run in background")
rootCmd.PersistentFlags().Int64VarP(&FilterPid, "pid", "p", 0, "specify pid to trace, default trace all process")
rootCmd.PersistentFlags().StringSliceVarP(&RemotePorts, common.RemotePortsVarName, "", []string{}, "specify remote ports to trace, default trace all")
rootCmd.PersistentFlags().StringSliceVarP(&LocalPorts, common.LocalPortsVarName, "", []string{}, "specify local ports to trace, default trace all")
rootCmd.PersistentFlags().StringSliceVarP(&RemoteIps, common.RemoteIpsVarName, "", []string{}, "specify remote ips to trace, default trace all")
// rootCmd.PersistentFlags().BoolVarP(&Verbose, "verbose", "v", false, "print verbose message")
rootCmd.PersistentFlags().StringVar(&IfName, "ifname", "eth0", "--ifname eth0")
rootCmd.PersistentFlags().MarkHidden("compatible")
rootCmd.PersistentFlags().StringVar(&BTFFilePath, "btf", "", "btf file path")
rootCmd.PersistentFlags().Int64VarP(&FilterPid, "pid", "p", 0, "Filter by pid")
rootCmd.PersistentFlags().StringSliceVarP(&RemotePorts, common.RemotePortsVarName, "", []string{}, "Filter by remote ports, seperate by ','")
rootCmd.PersistentFlags().StringSliceVarP(&LocalPorts, common.LocalPortsVarName, "", []string{}, "Filter by local ports, seperate by ','")
rootCmd.PersistentFlags().StringSliceVarP(&RemoteIps, common.RemoteIpsVarName, "", []string{}, "Filter by remote ips, seperate by ','")
// rootCmd.PersistentFlags().StringVar(&IfName, "ifname", "eth0", "--ifname eth0")
rootCmd.PersistentFlags().StringVar(&BTFFilePath, "btf", "", "specify kernel BTF file")

// log config
rootCmd.PersistentFlags().BoolVarP(&Debug, "debug", "d", false, "print more logs helpful to debug")
rootCmd.PersistentFlags().Int32Var(&DefaultLogLevel, "default-log-level", 3, "specify default log level, from 1(fatal level) to 5(debug level)")
rootCmd.PersistentFlags().Int32Var(&AgentLogLevel, "agent-log-level", 0, "specify agent module log level individually")
rootCmd.PersistentFlags().Int32Var(&BPFEventLogLevel, "bpf-event-log-level", 0, "specify bpf event log level individually")
rootCmd.PersistentFlags().Int32Var(&ConntrackLogLevel, "conntrack-log-level", 0, "specify conntrack module log level individually")
rootCmd.PersistentFlags().Int32Var(&ProtocolLogLevel, "protocol-log-level", 0, "specify protocol module log level individually")

// internal
rootCmd.PersistentFlags().IntVar(&BPFVerifyLogSize, "bpf-verify-log-size", 1*1024*1024, "--bpf-verify-log-size 1024")
rootCmd.PersistentFlags().IntVar(&KernEvtPerfEventBufferSize, "kern-perf-event-buffer-size", 1*1024*1024, "--kern-perf-event-buffer-size 1024")
rootCmd.PersistentFlags().IntVar(&KernEvtPerfEventBufferSize, "data-perf-event-buffer-size", 30*1024*1024, "--data-perf-event-buffer-size 1024")

// log config
rootCmd.PersistentFlags().BoolVarP(&Debug, "debug", "d", false, "print more logs helpful to debug")
rootCmd.PersistentFlags().Int32Var(&DefaultLogLevel, "default-log-level", 3, "--default-log-level 4 # specify default log level, from 1(fatal level) to 5(debug level)")
rootCmd.PersistentFlags().Int32Var(&AgentLogLevel, "agent-log-level", 0, "--agent-log-level 4 # specify agent module log level individually")
rootCmd.PersistentFlags().Int32Var(&BPFEventLogLevel, "bpf-event-log-level", 0, "--bpf-event-log-level 4 # specify bpf event log level individually")
rootCmd.PersistentFlags().Int32Var(&ConntrackLogLevel, "conntrack-log-level", 0, "--conntrack-log-level 4 # specify conntrack module log level individually")
rootCmd.PersistentFlags().Int32Var(&ProtocolLogLevel, "protocol-log-level", 0, "--protocol-log-level 4 # specify protocol module log level individually")
rootCmd.PersistentFlags().MarkHidden("default-log-level")
rootCmd.PersistentFlags().MarkHidden("agent-log-level")
rootCmd.PersistentFlags().MarkHidden("bpf-event-log-level")
rootCmd.PersistentFlags().MarkHidden("conntrack-log-level")
rootCmd.PersistentFlags().MarkHidden("protocol-log-level")
rootCmd.PersistentFlags().MarkHidden("bpf-verify-log-size")
rootCmd.PersistentFlags().MarkHidden("kern-perf-event-buffer-size")
rootCmd.PersistentFlags().MarkHidden("data-perf-event-buffer-size")

rootCmd.Flags().SortFlags = false
rootCmd.PersistentFlags().SortFlags = false
viper.BindPFlags(rootCmd.Flags())
Expand Down
74 changes: 57 additions & 17 deletions cmd/stat.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,33 @@ import (
)

var statCmd = &cobra.Command{
Use: "stat [-m pqtsn] [-s 10] [-g conn|remote-ip|remote-port|local-port|protocol]",
Short: "Analysis connections statistics",
Use: "stat [--metrics pqtsn] [--samples 10] [--group-by conn|remote-ip|remote-port|local-port|protocol] [--sort-by avg|max|p50|p90|p99]",
Short: "Analysis connections statistics. Aggregate metrics such as latency and size for request-response pairs.",
Example: `
# Basic Usage, only count HTTP connections, print results when press 'ctlc+c'
sudo kyanos stat http
# Print results 5 seconds periodically
sudo kyanos stat http -i 5
# Find the most slowly remote http server with '/example' api
sudo kyanos stat http --metrics t --group-by remote-ip --side client --path /example
# Same as above but also prints 3 slowest samples with full body
sudo kyanos stat http --metrics t --samples 3 --full-body ...
# Specify two metrics total duration & request size
sudo kyanos stat http --metrics tq --group-by remote-ip
# Sort by p99 of total duration's (default is 'avg')
sudo kyanos stat http --metrics t --sort-by p99
# Limit the number of output connection results to 1.
sudo kyanos stat http --metrics t --limit 1
# In addition to request-response time, also track request-response size.
sudo kyanos stat http --metrics tqp
`,
PersistentPreRun: func(cmd *cobra.Command, args []string) { Mode = AnalysisMode },
Run: func(cmd *cobra.Command, args []string) {
startAgent(agent.AgentOptions{LatencyFilter: initLatencyFilter(cmd), SizeFilter: initSizeFilter(cmd)})
Expand All @@ -24,7 +49,7 @@ var groupBy string
var interval int
var sortByPar string
var fullBody bool
var SUPPORTED_METRICS = []byte{'t', 'q', 'p', 'n', 's'}
var SUPPORTED_METRICS = []byte{'t', 'q', 'p', 'n', 's', 'i'}

func validateEnabledMetricsString() error {
for _, m := range []byte(enabledMetricsString) {
Expand Down Expand Up @@ -54,7 +79,7 @@ func createAnalysisOptions() (analysis.AnalysisOptions, error) {
if slices.Contains(enabledMetricsBytes, 'p') {
options.EnabledMetricTypeSet[analysis.ResponseSize] = true
}
if slices.Contains(enabledMetricsBytes, 'n') {
if slices.Contains(enabledMetricsBytes, 'n') || slices.Contains(enabledMetricsBytes, 'i') {
options.EnabledMetricTypeSet[analysis.BlackBoxDuration] = true
}
if slices.Contains(enabledMetricsBytes, 's') {
Expand Down Expand Up @@ -86,7 +111,7 @@ func createAnalysisOptions() (analysis.AnalysisOptions, error) {
case "P99":
options.SortBy = analysis.P99
default:
logger.Warnf("unknown --sort flag: %s, use default '%s'", sortByPar, "avg")
logger.Warnf("unknown --sort-by flag: %s, use default '%s'", sortByPar, "avg")
options.SortBy = analysis.Avg
}

Expand All @@ -95,19 +120,34 @@ func createAnalysisOptions() (analysis.AnalysisOptions, error) {
}
return options, nil
}

func init() {
statCmd.PersistentFlags().StringVarP(&enabledMetricsString, "metrics", "m", "t", "-m pqtsn")
statCmd.PersistentFlags().IntVarP(&sampleCount, "sample", "s", 0, "-s 10")
statCmd.PersistentFlags().IntVarP(&displayLimit, "limit", "l", 10, "-l 20")
statCmd.PersistentFlags().IntVarP(&interval, "interval", "i", 0, "-i 5")
statCmd.PersistentFlags().StringVarP(&groupBy, "group-by", "g", "remote-ip", "-g remote-ip")
statCmd.PersistentFlags().Float64("latency", 0, "--latency 100 # millseconds")
statCmd.PersistentFlags().Int64("req-size", 0, "--req-size 1024 # bytes")
statCmd.PersistentFlags().Int64("resp-size", 0, "--resp-size 1024 # bytes")
statCmd.PersistentFlags().StringVar(&SidePar, "side", "all", "--side client|all|server")
statCmd.PersistentFlags().StringVar(&sortByPar, "sort", "avg", "--sort avg|max|p50|p90|p99")
statCmd.PersistentFlags().BoolVar(&fullBody, "full-body", false, "--full-body")
statCmd.PersistentFlags().StringVarP(&enabledMetricsString, "metrics", "m", "t", `Specify the statistical dimensions, including:
t: total time taken for request response,
q: request size,
p: response size,
n: network device latency,
i: internal application latency,
s: time spent reading from the socket buffer
You can specify these flags individually or
combine them together like: '-m pq'`)
statCmd.PersistentFlags().IntVarP(&sampleCount, "samples", "s", 0,
"Specify the number of samples to be attached for each result.\n"+
"By default, only a summary is output.\n"+
"refer to the '--full-body' option.")
statCmd.PersistentFlags().BoolVar(&fullBody, "full-body", false, "Used with '--samples' option, print content of req-resp when print samples.")
statCmd.PersistentFlags().IntVarP(&displayLimit, "limit", "l", 10, "Specify the number of output results.")
statCmd.PersistentFlags().IntVarP(&interval, "interval", "i", 0, "Print statistics periodically, or if not specified, statistics will be displayed when stopped with `ctrl+c`.")
statCmd.PersistentFlags().StringVarP(&groupBy, "group-by", "g", "remote-ip",
"Specify aggregation dimension: \n"+
"('conn', 'local-port', 'remote-port', 'remote-ip', 'protocol', 'http-path', 'none')\n"+
"note: 'none' is aggregate all req-resp pair together")
statCmd.PersistentFlags().StringVar(&sortByPar, "sort-by", "avg", "Specify the sorting method for the output results: ('avg', 'max', 'p50', 'p90', 'p99'")

// common
statCmd.PersistentFlags().Float64("latency", 0, "Filter based on request response time")
statCmd.PersistentFlags().Int64("req-size", 0, "Filter based on request bytes size")
statCmd.PersistentFlags().Int64("resp-size", 0, "Filter based on response bytes size")
statCmd.PersistentFlags().StringVar(&SidePar, "side", "all", "Filter based on connection side. can be: server | client")

statCmd.Flags().SortFlags = false
statCmd.PersistentFlags().SortFlags = false
Expand Down
20 changes: 0 additions & 20 deletions cmd/version.go

This file was deleted.

21 changes: 13 additions & 8 deletions cmd/watch.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,14 @@ import (
)

var watchCmd = &cobra.Command{
Use: "watch [http|redis|mysql] [filter]",
Short: "Watch the request/response pair and print to the console",
Long: `It is possible to filter network requests based on specific protocol and print the request/response data to the console. `,
Use: "watch [http|redis|mysql [flags]]",
Example: `
sudo kyanos watch
sudo kyanos watch http --side server --pid 1234 --path /foo/bar --host ubuntu.com
sudo kyanos watch redis --comands GET,SET --keys foo,bar --key-prefix app1:
sudo kyanos watch mysql --latency 100 --req-size 1024 --resp-size 2048
`,
Short: "Watch the request/response pair and print body to the console",
PersistentPreRun: func(cmd *cobra.Command, args []string) { Mode = WatchMode },
Run: func(cmd *cobra.Command, args []string) {
list, err := cmd.Flags().GetBool("list")
Expand All @@ -27,11 +32,11 @@ var watchCmd = &cobra.Command{
}

func init() {
watchCmd.Flags().BoolP("list", "l", false, "--list # list all support protocols")
watchCmd.PersistentFlags().Float64("latency", 0, "--latency 100 # millseconds")
watchCmd.PersistentFlags().Int64("req-size", 0, "--req-size 1024 # bytes")
watchCmd.PersistentFlags().Int64("resp-size", 0, "--resp-size 1024 # bytes")
watchCmd.PersistentFlags().StringVar(&SidePar, "side", "all", "--side client|all|server")
watchCmd.Flags().BoolP("list", "l", false, "list all support protocols")
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().StringVar(&SidePar, "side", "all", "Filter based on connection side. can be: server | client")
watchCmd.Flags().SortFlags = false
watchCmd.PersistentFlags().SortFlags = false
rootCmd.AddCommand(watchCmd)
Expand Down
Binary file added docs/banner.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.

0 comments on commit c4ff3fa

Please sign in to comment.