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 }