github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/worker/logsender/worker.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package logsender 5 6 import ( 7 "fmt" 8 9 "github.com/juju/errors" 10 "github.com/juju/loggo" 11 "github.com/juju/worker/v3" 12 13 "github.com/juju/juju/api/logsender" 14 "github.com/juju/juju/rpc/params" 15 jworker "github.com/juju/juju/worker" 16 ) 17 18 const loggerName = "juju.worker.logsender" 19 20 // New starts a logsender worker which reads log message structs from 21 // a channel and sends them to the controller via the logsink API. 22 func New(logs LogRecordCh, logSenderAPI *logsender.API) worker.Worker { 23 loop := func(stop <-chan struct{}) error { 24 // It has been observed that sometimes the logsender.API gets wedged 25 // attempting to get the LogWriter while the agent is being torn down, 26 // and the call to logSenderAPI.LogWriter() doesn't return. This stops 27 // the logsender worker from shutting down, and causes the entire 28 // agent to get wedged. To mitigate this, we get the LogWriter in a 29 // different goroutine allowing the worker to interrupt this. 30 sender := make(chan logsender.LogWriter) 31 errChan := make(chan error) 32 go func() { 33 logWriter, err := logSenderAPI.LogWriter() 34 if err != nil { 35 select { 36 case errChan <- err: 37 case <-stop: 38 } 39 return 40 } 41 select { 42 case sender <- logWriter: 43 case <-stop: 44 logWriter.Close() 45 } 46 }() 47 var logWriter logsender.LogWriter 48 var err error 49 select { 50 case logWriter = <-sender: 51 case err = <-errChan: 52 return errors.Annotate(err, "logsender dial failed") 53 case <-stop: 54 return nil 55 } 56 defer logWriter.Close() 57 for { 58 select { 59 case rec := <-logs: 60 err := logWriter.WriteLog(¶ms.LogRecord{ 61 Time: rec.Time, 62 Module: rec.Module, 63 Location: rec.Location, 64 Level: rec.Level.String(), 65 Message: rec.Message, 66 Labels: rec.Labels, 67 }) 68 if err != nil { 69 return errors.Trace(err) 70 } 71 if rec.DroppedAfter > 0 { 72 // If messages were dropped after this one, report 73 // the count (the source of the log messages - 74 // BufferedLogWriter - handles the actual dropping 75 // and counting). 76 // 77 // Any logs indicated as dropped here are will 78 // never end up in the logs DB in the JES 79 // (although will still be in the local agent log 80 // file). Message dropping by the 81 // BufferedLogWriter is last resort protection 82 // against memory exhaustion and should only 83 // happen if API connectivity is lost for extended 84 // periods. The maximum in-memory log buffer is 85 // quite large (see the InstallBufferedLogWriter 86 // call in jujuDMain). 87 err := logWriter.WriteLog(¶ms.LogRecord{ 88 Time: rec.Time, 89 Module: loggerName, 90 Level: loggo.WARNING.String(), 91 Message: fmt.Sprintf("%d log messages dropped due to lack of API connectivity", rec.DroppedAfter), 92 }) 93 if err != nil { 94 return errors.Trace(err) 95 } 96 } 97 98 case <-stop: 99 return nil 100 } 101 } 102 } 103 return jworker.NewSimpleWorker(loop) 104 }