github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/client/cli/debug/debug.go (about)

     1  package debug
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"errors"
     7  	"fmt"
     8  	"sort"
     9  	"strings"
    10  	"text/tabwriter"
    11  	"time"
    12  
    13  	"github.com/tickoalcantara12/micro/v3/client/cli/namespace"
    14  	"github.com/tickoalcantara12/micro/v3/client/cli/util"
    15  	"github.com/tickoalcantara12/micro/v3/cmd"
    16  	proto "github.com/tickoalcantara12/micro/v3/proto/debug"
    17  	"github.com/tickoalcantara12/micro/v3/service/client"
    18  	"github.com/tickoalcantara12/micro/v3/service/registry"
    19  	"github.com/urfave/cli/v2"
    20  )
    21  
    22  func init() {
    23  	subcommands := []*cli.Command{
    24  		&cli.Command{
    25  			Name:   "health",
    26  			Usage:  `Get the service health`,
    27  			Action: util.Print(QueryHealth),
    28  		},
    29  		&cli.Command{
    30  			Name:   "stats",
    31  			Usage:  "Query the stats of specified service(s), e.g micro stats srv1 srv2 srv3",
    32  			Action: util.Print(queryStats),
    33  			Flags: []cli.Flag{
    34  				&cli.StringFlag{
    35  					Name:  "all",
    36  					Usage: "to list all builtin services use --all builtin, for user's services use --all custom",
    37  				},
    38  			},
    39  		},
    40  	}
    41  
    42  	command := &cli.Command{
    43  		Name:        "debug",
    44  		Usage:       "Debug a service",
    45  		Action:      func(ctx *cli.Context) error { return nil },
    46  		Subcommands: subcommands,
    47  	}
    48  
    49  	cmd.Register(command)
    50  }
    51  
    52  // QueryStats returns stats of specified service(s)
    53  func QueryStats(c *cli.Context, args []string) ([]byte, error) {
    54  	if c.String("all") == "builtin" {
    55  
    56  		sl, err := ListServices(c, args)
    57  		if err != nil {
    58  			return nil, err
    59  		}
    60  
    61  		servList := strings.Split(string(sl), "\n")
    62  		var builtinList []string
    63  
    64  		for _, s := range servList {
    65  
    66  			if util.IsBuiltInService(s) {
    67  				builtinList = append(builtinList, s)
    68  			}
    69  		}
    70  
    71  		c.Set("all", "")
    72  
    73  		if len(builtinList) == 0 {
    74  			return nil, errors.New("no builtin service(s) found")
    75  		}
    76  
    77  		return QueryStats(c, builtinList)
    78  	}
    79  
    80  	if c.String("all") == "custom" {
    81  		sl, err := ListServices(c, args)
    82  		if err != nil {
    83  			return nil, err
    84  		}
    85  
    86  		servList := strings.Split(string(sl), "\n")
    87  		var customList []string
    88  
    89  		for _, s := range servList {
    90  
    91  			// temporary excluding server
    92  			if s == "server" {
    93  				continue
    94  			}
    95  
    96  			if !util.IsBuiltInService(s) {
    97  				customList = append(customList, s)
    98  			}
    99  		}
   100  
   101  		c.Set("all", "")
   102  
   103  		if len(customList) == 0 {
   104  			return nil, errors.New("no custom service(s) found")
   105  		}
   106  
   107  		return QueryStats(c, customList)
   108  	}
   109  
   110  	if len(args) == 0 {
   111  		return nil, cli.ShowSubcommandHelp(c)
   112  	}
   113  
   114  	env, err := util.GetEnv(c)
   115  	if err != nil {
   116  		return nil, err
   117  	}
   118  	ns, err := namespace.Get(env.Name)
   119  	if err != nil {
   120  		return nil, err
   121  	}
   122  
   123  	titlesList := []string{"NODE", "ADDRESS:PORT", "STARTED", "UPTIME", "MEMORY", "THREADS", "GC"}
   124  	titles := strings.Join(titlesList, "\t")
   125  
   126  	var buf bytes.Buffer
   127  	w := tabwriter.NewWriter(&buf, 0, 1, 4, ' ', tabwriter.TabIndent)
   128  
   129  	for _, a := range args {
   130  
   131  		service, err := registry.DefaultRegistry.GetService(a, registry.GetDomain(ns))
   132  		if err != nil {
   133  			return nil, err
   134  		}
   135  		if len(service) == 0 {
   136  			return nil, errors.New("Service not found")
   137  		}
   138  
   139  		req := client.NewRequest(service[0].Name, "Debug.Stats", &proto.StatsRequest{})
   140  
   141  		fmt.Fprintln(w, "SERVICE\t"+service[0].Name+"\n")
   142  
   143  		for _, serv := range service {
   144  
   145  			fmt.Fprintln(w, "VERSION\t"+serv.Version+"\n")
   146  			fmt.Fprintln(w, titles)
   147  
   148  			// query health for every node
   149  			for _, node := range serv.Nodes {
   150  				address := node.Address
   151  				rsp := &proto.StatsResponse{}
   152  
   153  				var err error
   154  
   155  				// call using client
   156  				err = client.DefaultClient.Call(context.Background(), req, rsp, client.WithAddress(address))
   157  
   158  				var started, uptime, memory, gc string
   159  				if err == nil {
   160  					started = time.Unix(int64(rsp.Started), 0).Format("Jan 2 15:04:05")
   161  					uptime = fmt.Sprintf("%v", time.Duration(rsp.Uptime)*time.Second)
   162  					memory = fmt.Sprintf("%.2fmb", float64(rsp.Memory)/(1024.0*1024.0))
   163  					gc = fmt.Sprintf("%v", time.Duration(rsp.Gc))
   164  				}
   165  
   166  				line := fmt.Sprintf("%s\t%s\t%s\t%s\t%s\t%d\t%s\n",
   167  					node.Id, node.Address, started, uptime, memory, rsp.Threads, gc)
   168  
   169  				fmt.Fprintln(w, line)
   170  			}
   171  		}
   172  	}
   173  
   174  	w.Flush()
   175  
   176  	return buf.Bytes(), nil
   177  }
   178  
   179  func ListServices(c *cli.Context, args []string) ([]byte, error) {
   180  	var rsp []*registry.Service
   181  	var err error
   182  
   183  	env, err := util.GetEnv(c)
   184  	if err != nil {
   185  		return nil, err
   186  	}
   187  	ns, err := namespace.Get(env.Name)
   188  	if err != nil {
   189  		return nil, err
   190  	}
   191  
   192  	rsp, err = registry.DefaultRegistry.ListServices(registry.ListDomain(ns))
   193  	if err != nil {
   194  		return nil, err
   195  	}
   196  
   197  	var services []string
   198  	for _, service := range rsp {
   199  		services = append(services, service.Name)
   200  	}
   201  
   202  	sort.Strings(services)
   203  
   204  	return []byte(strings.Join(services, "\n")), nil
   205  }
   206  
   207  func QueryHealth(c *cli.Context, args []string) ([]byte, error) {
   208  	if len(args) == 0 {
   209  		return nil, errors.New("require service name")
   210  	}
   211  
   212  	env, err := util.GetEnv(c)
   213  	if err != nil {
   214  		return nil, err
   215  	}
   216  	ns, err := namespace.Get(env.Name)
   217  	if err != nil {
   218  		return nil, err
   219  	}
   220  
   221  	req := client.NewRequest(args[0], "Debug.Health", &proto.HealthRequest{})
   222  
   223  	// if the address is specified then we just call it
   224  	if addr := c.String("address"); len(addr) > 0 {
   225  		rsp := &proto.HealthResponse{}
   226  		err := client.DefaultClient.Call(
   227  			context.Background(),
   228  			req,
   229  			rsp,
   230  			client.WithAddress(addr),
   231  		)
   232  		if err != nil {
   233  			return nil, err
   234  		}
   235  		return []byte(rsp.Status), nil
   236  	}
   237  
   238  	// otherwise get the service and call each instance individually
   239  	service, err := registry.DefaultRegistry.GetService(args[0], registry.GetDomain(ns))
   240  	if err != nil {
   241  		return nil, err
   242  	}
   243  
   244  	if len(service) == 0 {
   245  		return nil, errors.New("Service not found")
   246  	}
   247  
   248  	var output []string
   249  	// print things
   250  	output = append(output, "service  "+service[0].Name)
   251  
   252  	for _, serv := range service {
   253  		// print things
   254  		output = append(output, "\nversion "+serv.Version)
   255  		output = append(output, "\nnode\t\taddress:port\t\tstatus")
   256  
   257  		// query health for every node
   258  		for _, node := range serv.Nodes {
   259  			address := node.Address
   260  			rsp := &proto.HealthResponse{}
   261  
   262  			var err error
   263  
   264  			// call using client
   265  			err = client.DefaultClient.Call(
   266  				context.Background(),
   267  				req,
   268  				rsp,
   269  				client.WithAddress(address),
   270  			)
   271  
   272  			var status string
   273  			if err != nil {
   274  				status = err.Error()
   275  			} else {
   276  				status = rsp.Status
   277  			}
   278  			output = append(output, fmt.Sprintf("%s\t\t%s\t\t%s", node.Id, node.Address, status))
   279  		}
   280  	}
   281  
   282  	return []byte(strings.Join(output, "\n")), nil
   283  }
   284  
   285  func queryHealth(c *cli.Context, args []string) ([]byte, error) {
   286  	return QueryHealth(c, args)
   287  }
   288  
   289  func queryStats(c *cli.Context, args []string) ([]byte, error) {
   290  	return QueryStats(c, args)
   291  }