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  }