github.com/anth0d/nomad@v0.0.0-20221214183521-ae3a0a2cad06/command/agent/monitor/monitor.go (about) 1 package monitor 2 3 import ( 4 "fmt" 5 "sync" 6 "time" 7 8 log "github.com/hashicorp/go-hclog" 9 "github.com/hashicorp/nomad/helper" 10 ) 11 12 // Monitor provides a mechanism to stream logs using go-hclog 13 // InterceptLogger and SinkAdapter. It allows streaming of logs 14 // at a different log level than what is set on the logger. 15 type Monitor interface { 16 // Start returns a channel of log messages which are sent 17 // ever time a log message occurs 18 Start() <-chan []byte 19 20 // Stop de-registers the sink from the InterceptLogger 21 // and closes the log channels 22 Stop() 23 } 24 25 // monitor implements the Monitor interface 26 type monitor struct { 27 // protects droppedCount and logCh 28 sync.Mutex 29 30 sink log.SinkAdapter 31 32 // logger is the logger we will be monitoring 33 logger log.InterceptLogger 34 35 // logCh is a buffered chan where we send logs when streaming 36 logCh chan []byte 37 38 // doneCh coordinates the shutdown of logCh 39 doneCh chan struct{} 40 41 // droppedCount is the current count of messages 42 // that were dropped from the logCh buffer. 43 // only access under lock 44 droppedCount int 45 bufSize int 46 // droppedDuration is the amount of time we should 47 // wait to check for dropped messages. Defaults 48 // to 3 seconds 49 droppedDuration time.Duration 50 } 51 52 // New creates a new Monitor. Start must be called in order to actually start 53 // streaming logs 54 func New(buf int, logger log.InterceptLogger, opts *log.LoggerOptions) Monitor { 55 return new(buf, logger, opts) 56 } 57 58 func new(buf int, logger log.InterceptLogger, opts *log.LoggerOptions) *monitor { 59 sw := &monitor{ 60 logger: logger, 61 logCh: make(chan []byte, buf), 62 doneCh: make(chan struct{}, 1), 63 bufSize: buf, 64 droppedDuration: 3 * time.Second, 65 } 66 67 opts.Output = sw 68 sink := log.NewSinkAdapter(opts) 69 sw.sink = sink 70 71 return sw 72 } 73 74 // Stop deregisters the sink and stops the monitoring process 75 func (d *monitor) Stop() { 76 d.logger.DeregisterSink(d.sink) 77 close(d.doneCh) 78 } 79 80 // Start registers a sink on the monitor's logger and starts sending 81 // received log messages over the returned channel. 82 func (d *monitor) Start() <-chan []byte { 83 // register our sink with the logger 84 d.logger.RegisterSink(d.sink) 85 86 streamCh := make(chan []byte, d.bufSize) 87 88 // run a go routine that listens for streamed 89 // log messages and sends them to streamCh 90 go func() { 91 defer close(streamCh) 92 93 for { 94 select { 95 case log := <-d.logCh: 96 select { 97 case <-d.doneCh: 98 return 99 case streamCh <- log: 100 } 101 case <-d.doneCh: 102 return 103 } 104 } 105 }() 106 107 // run a go routine that periodically checks for 108 // dropped messages and makes room on the logCh 109 // to add a dropped message count warning 110 go func() { 111 timer, stop := helper.NewSafeTimer(d.droppedDuration) 112 defer stop() 113 114 // loop and check for dropped messages 115 for { 116 timer.Reset(d.droppedDuration) 117 118 select { 119 case <-d.doneCh: 120 return 121 case <-timer.C: 122 d.Lock() 123 124 // Check if there have been any dropped messages. 125 if d.droppedCount > 0 { 126 dropped := fmt.Sprintf("[WARN] Monitor dropped %d logs during monitor request\n", d.droppedCount) 127 select { 128 case <-d.doneCh: 129 d.Unlock() 130 return 131 // Try sending dropped message count to logCh in case 132 // there is room in the buffer now. 133 case d.logCh <- []byte(dropped): 134 default: 135 // Drop a log message to make room for "Monitor dropped.." message 136 select { 137 case <-d.logCh: 138 d.droppedCount++ 139 dropped = fmt.Sprintf("[WARN] Monitor dropped %d logs during monitor request\n", d.droppedCount) 140 default: 141 } 142 d.logCh <- []byte(dropped) 143 } 144 d.droppedCount = 0 145 } 146 // unlock after handling dropped message 147 d.Unlock() 148 } 149 } 150 }() 151 152 return streamCh 153 } 154 155 // Write attempts to send latest log to logCh 156 // it drops the log if channel is unavailable to receive 157 func (d *monitor) Write(p []byte) (n int, err error) { 158 d.Lock() 159 defer d.Unlock() 160 161 // ensure logCh is still open 162 select { 163 case <-d.doneCh: 164 return 165 default: 166 } 167 168 bytes := make([]byte, len(p)) 169 copy(bytes, p) 170 171 select { 172 case d.logCh <- bytes: 173 default: 174 d.droppedCount++ 175 } 176 177 return len(p), nil 178 }