github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/worker/common/charmrunner/logger.go (about)

     1  // Copyright 2012-2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package charmrunner
     5  
     6  import (
     7  	"bufio"
     8  	"io"
     9  	"sync"
    10  	"time"
    11  
    12  	"github.com/juju/loggo"
    13  )
    14  
    15  var logger = loggo.GetLogger("juju.worker.common.runner")
    16  
    17  // NewHookLogger creates a new hook logger.
    18  func NewHookLogger(logger loggo.Logger, outReader io.ReadCloser) *HookLogger {
    19  	return &HookLogger{
    20  		r:      outReader,
    21  		done:   make(chan struct{}),
    22  		logger: logger,
    23  	}
    24  }
    25  
    26  // HookLogger streams the output from a hook to a logger.
    27  type HookLogger struct {
    28  	r       io.ReadCloser
    29  	done    chan struct{}
    30  	mu      sync.Mutex
    31  	stopped bool
    32  	logger  loggo.Logger
    33  }
    34  
    35  // Run starts the hook logger.
    36  func (l *HookLogger) Run() {
    37  	defer close(l.done)
    38  	defer l.r.Close()
    39  	br := bufio.NewReaderSize(l.r, 4096)
    40  	for {
    41  		line, _, err := br.ReadLine()
    42  		if err != nil {
    43  			if err != io.EOF {
    44  				logger.Errorf("cannot read hook output: %v", err)
    45  			}
    46  			break
    47  		}
    48  		l.mu.Lock()
    49  		if l.stopped {
    50  			l.mu.Unlock()
    51  			return
    52  		}
    53  		l.logger.Debugf("%s", line)
    54  		l.mu.Unlock()
    55  	}
    56  }
    57  
    58  // Stop stops the hook logger.
    59  func (l *HookLogger) Stop() {
    60  	// We can see the process exit before the logger has processed
    61  	// all its output, so allow a moment for the data buffered
    62  	// in the pipe to be processed. We don't wait indefinitely though,
    63  	// because the hook may have started a background process
    64  	// that keeps the pipe open.
    65  	select {
    66  	case <-l.done:
    67  	case <-time.After(100 * time.Millisecond):
    68  	}
    69  	// We can't close the pipe asynchronously, so just
    70  	// stifle output instead.
    71  	l.mu.Lock()
    72  	l.stopped = true
    73  	l.mu.Unlock()
    74  }