github.com/imannamdari/v2ray-core/v5@v5.0.5/main/commands/all/api/stats.go (about)

     1  package api
     2  
     3  import (
     4  	"fmt"
     5  	"os"
     6  	"sort"
     7  	"strings"
     8  	"time"
     9  
    10  	statsService "github.com/imannamdari/v2ray-core/v5/app/stats/command"
    11  	"github.com/imannamdari/v2ray-core/v5/common/units"
    12  	"github.com/imannamdari/v2ray-core/v5/main/commands/base"
    13  )
    14  
    15  var cmdStats = &base.Command{
    16  	CustomFlags: true,
    17  	UsageLine:   "{{.Exec}} api stats [--server=127.0.0.1:8080] [pattern]...",
    18  	Short:       "query statistics",
    19  	Long: `
    20  Query statistics from V2Ray.
    21  
    22  > Make sure you have "StatsService" set in "config.api.services" 
    23  of server config.
    24  
    25  Arguments:
    26  
    27  	-regexp
    28  		The patterns are using regexp.
    29  
    30  	-reset
    31  		Reset counters to 0 after fetching their values.
    32  
    33  	-runtime
    34  		Get runtime statistics.
    35  
    36  	-json
    37  		Use json output.
    38  
    39  	-s, -server <server:port>
    40  		The API server address. Default 127.0.0.1:8080
    41  
    42  	-t, -timeout <seconds>
    43  		Timeout seconds to call API. Default 3
    44  
    45  Example:
    46  
    47  	{{.Exec}} {{.LongName}} -runtime
    48  	{{.Exec}} {{.LongName}} node1
    49  	{{.Exec}} {{.LongName}} -json node1 node2
    50  	{{.Exec}} {{.LongName}} -regexp 'node1.+downlink'
    51  `,
    52  	Run: executeStats,
    53  }
    54  
    55  func executeStats(cmd *base.Command, args []string) {
    56  	setSharedFlags(cmd)
    57  	var (
    58  		runtime bool
    59  		regexp  bool
    60  		reset   bool
    61  	)
    62  	cmd.Flag.BoolVar(&runtime, "runtime", false, "")
    63  	cmd.Flag.BoolVar(&regexp, "regexp", false, "")
    64  	cmd.Flag.BoolVar(&reset, "reset", false, "")
    65  	cmd.Flag.Parse(args)
    66  	unnamed := cmd.Flag.Args()
    67  	if runtime {
    68  		getRuntimeStats(apiJSON)
    69  		return
    70  	}
    71  	getStats(unnamed, regexp, reset, apiJSON)
    72  }
    73  
    74  func getRuntimeStats(jsonOutput bool) {
    75  	conn, ctx, close := dialAPIServer()
    76  	defer close()
    77  
    78  	client := statsService.NewStatsServiceClient(conn)
    79  	r := &statsService.SysStatsRequest{}
    80  	resp, err := client.GetSysStats(ctx, r)
    81  	if err != nil {
    82  		base.Fatalf("failed to get sys stats: %s", err)
    83  	}
    84  	if jsonOutput {
    85  		showJSONResponse(resp)
    86  		return
    87  	}
    88  	showRuntimeStats(resp)
    89  }
    90  
    91  func showRuntimeStats(s *statsService.SysStatsResponse) {
    92  	formats := []string{"%-22s", "%-10s"}
    93  	rows := [][]string{
    94  		{"Up time", (time.Duration(s.Uptime) * time.Second).String()},
    95  		{"Memory obtained", units.ByteSize(s.Sys).String()},
    96  		{"Number of goroutines", fmt.Sprintf("%d", s.NumGoroutine)},
    97  		{"Heap allocated", units.ByteSize(s.Alloc).String()},
    98  		{"Live objects", fmt.Sprintf("%d", s.LiveObjects)},
    99  		{"Heap allocated total", units.ByteSize(s.TotalAlloc).String()},
   100  		{"Heap allocate count", fmt.Sprintf("%d", s.Mallocs)},
   101  		{"Heap free count", fmt.Sprintf("%d", s.Frees)},
   102  		{"Number of GC", fmt.Sprintf("%d", s.NumGC)},
   103  		{"Time of GC pause", (time.Duration(s.PauseTotalNs) * time.Nanosecond).String()},
   104  	}
   105  	sb := new(strings.Builder)
   106  	writeRow(sb, 0, 0,
   107  		[]string{"Item", "Value"},
   108  		formats,
   109  	)
   110  	for i, r := range rows {
   111  		writeRow(sb, 0, i+1, r, formats)
   112  	}
   113  	os.Stdout.WriteString(sb.String())
   114  }
   115  
   116  func getStats(patterns []string, regexp, reset, jsonOutput bool) {
   117  	conn, ctx, close := dialAPIServer()
   118  	defer close()
   119  
   120  	client := statsService.NewStatsServiceClient(conn)
   121  	r := &statsService.QueryStatsRequest{
   122  		Patterns: patterns,
   123  		Regexp:   regexp,
   124  		Reset_:   reset,
   125  	}
   126  	resp, err := client.QueryStats(ctx, r)
   127  	if err != nil {
   128  		base.Fatalf("failed to query stats: %s", err)
   129  	}
   130  	if jsonOutput {
   131  		showJSONResponse(resp)
   132  		return
   133  	}
   134  	sort.Slice(resp.Stat, func(i, j int) bool {
   135  		return resp.Stat[i].Name < resp.Stat[j].Name
   136  	})
   137  	showStats(resp.Stat)
   138  }
   139  
   140  func showStats(stats []*statsService.Stat) {
   141  	if len(stats) == 0 {
   142  		return
   143  	}
   144  	formats := []string{"%-12s", "%s"}
   145  	sum := int64(0)
   146  	sb := new(strings.Builder)
   147  	idx := 0
   148  	writeRow(sb, 0, 0,
   149  		[]string{"Value", "Name"},
   150  		formats,
   151  	)
   152  	for _, stat := range stats {
   153  		// if stat.Value == 0 {
   154  		// 	continue
   155  		// }
   156  		idx++
   157  		sum += stat.Value
   158  		writeRow(
   159  			sb, 0, idx,
   160  			[]string{units.ByteSize(stat.Value).String(), stat.Name},
   161  			formats,
   162  		)
   163  	}
   164  	sb.WriteString(
   165  		fmt.Sprintf("\nTotal: %s\n", units.ByteSize(sum)),
   166  	)
   167  	os.Stdout.WriteString(sb.String())
   168  }