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(&params.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(&params.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  }