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