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