github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/command/agent_monitor.go (about) 1 package command 2 3 import ( 4 "fmt" 5 "io" 6 "os" 7 "os/signal" 8 "strconv" 9 "strings" 10 "syscall" 11 "time" 12 13 "github.com/hashicorp/nomad/api" 14 "github.com/mitchellh/cli" 15 ) 16 17 type MonitorCommand struct { 18 Meta 19 } 20 21 func (c *MonitorCommand) Help() string { 22 helpText := ` 23 Usage: nomad monitor [options] 24 25 Stream log messages of a nomad agent. The monitor command lets you 26 listen for log levels that may be filtered out of the Nomad agent. For 27 example your agent may only be logging at INFO level, but with the monitor 28 command you can set -log-level DEBUG 29 30 When ACLs are enabled, this command requires a token with the 'agent:read' 31 capability. 32 33 General Options: 34 35 ` + generalOptionsUsage(usageOptsDefault|usageOptsNoNamespace) + ` 36 37 Monitor Specific Options: 38 39 -log-level <level> 40 Sets the log level to monitor (default: INFO) 41 42 -node-id <node-id> 43 Sets the specific node to monitor 44 45 -server-id <server-id> 46 Sets the specific server to monitor 47 48 -json 49 Sets log output to JSON format 50 ` 51 return strings.TrimSpace(helpText) 52 } 53 54 func (c *MonitorCommand) Synopsis() string { 55 return "Stream logs from a Nomad agent" 56 } 57 58 func (c *MonitorCommand) Name() string { return "monitor" } 59 60 func (c *MonitorCommand) Run(args []string) int { 61 c.Ui = &cli.PrefixedUi{ 62 OutputPrefix: " ", 63 InfoPrefix: " ", 64 ErrorPrefix: "==> ", 65 Ui: c.Ui, 66 } 67 68 var logLevel string 69 var nodeID string 70 var serverID string 71 var logJSON bool 72 73 flags := c.Meta.FlagSet(c.Name(), FlagSetClient) 74 flags.Usage = func() { c.Ui.Output(c.Help()) } 75 flags.StringVar(&logLevel, "log-level", "", "") 76 flags.StringVar(&nodeID, "node-id", "", "") 77 flags.StringVar(&serverID, "server-id", "", "") 78 flags.BoolVar(&logJSON, "json", false, "") 79 80 if err := flags.Parse(args); err != nil { 81 return 1 82 } 83 84 args = flags.Args() 85 if l := len(args); l != 0 { 86 c.Ui.Error("This command takes no arguments") 87 c.Ui.Error(commandErrorText(c)) 88 return 1 89 } 90 91 client, err := c.Meta.Client() 92 if err != nil { 93 c.Ui.Error(fmt.Sprintf("Error initializing client: %s", err)) 94 c.Ui.Error(commandErrorText(c)) 95 return 1 96 } 97 98 // Query the node info and lookup prefix 99 if len(nodeID) == 1 { 100 c.Ui.Error("Node identifier must contain at least two characters.") 101 return 1 102 } 103 104 if nodeID != "" { 105 nodeID = sanitizeUUIDPrefix(nodeID) 106 nodes, _, err := client.Nodes().PrefixList(nodeID) 107 if err != nil { 108 c.Ui.Error(fmt.Sprintf("Error querying node: %v", err)) 109 return 1 110 } 111 112 if len(nodes) == 0 { 113 c.Ui.Error(fmt.Sprintf("No node(s) with prefix or id %q found", nodeID)) 114 return 1 115 } 116 117 if len(nodes) > 1 { 118 out := formatNodeStubList(nodes, false) 119 c.Ui.Output(fmt.Sprintf("Prefix matched multiple nodes\n\n%s", out)) 120 return 1 121 } 122 nodeID = nodes[0].ID 123 } 124 125 params := map[string]string{ 126 "log_level": logLevel, 127 "node_id": nodeID, 128 "server_id": serverID, 129 "log_json": strconv.FormatBool(logJSON), 130 } 131 132 query := &api.QueryOptions{ 133 Params: params, 134 } 135 136 eventDoneCh := make(chan struct{}) 137 frames, errCh := client.Agent().Monitor(eventDoneCh, query) 138 select { 139 case err := <-errCh: 140 c.Ui.Error(fmt.Sprintf("Error starting monitor: %s", err)) 141 c.Ui.Error(commandErrorText(c)) 142 return 1 143 default: 144 } 145 146 // Create a reader 147 var r io.ReadCloser 148 frameReader := api.NewFrameReader(frames, errCh, eventDoneCh) 149 frameReader.SetUnblockTime(500 * time.Millisecond) 150 r = frameReader 151 152 defer r.Close() 153 154 signalCh := make(chan os.Signal, 1) 155 signal.Notify(signalCh, os.Interrupt, syscall.SIGTERM) 156 157 go func() { 158 <-signalCh 159 // End the streaming 160 r.Close() 161 }() 162 163 _, err = io.Copy(os.Stdout, r) 164 if err != nil { 165 c.Ui.Error(fmt.Sprintf("error monitoring logs: %s", err)) 166 return 1 167 } 168 169 return 0 170 }