github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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 "gopkg.in/juju/worker.v1" 12 13 "github.com/juju/juju/api/logsender" 14 "github.com/juju/juju/apiserver/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 JES 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 return 47 }() 48 var logWriter logsender.LogWriter 49 var err error 50 select { 51 case logWriter = <-sender: 52 case err = <-errChan: 53 return errors.Annotate(err, "logsender dial failed") 54 case <-stop: 55 return nil 56 } 57 defer logWriter.Close() 58 for { 59 select { 60 case rec := <-logs: 61 err := logWriter.WriteLog(¶ms.LogRecord{ 62 Time: rec.Time, 63 Module: rec.Module, 64 Location: rec.Location, 65 Level: rec.Level.String(), 66 Message: rec.Message, 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 }