github.com/dpiddy/docker@v1.12.2-rc1/daemon/logger/copier.go (about)

     1  package logger
     2  
     3  import (
     4  	"bufio"
     5  	"bytes"
     6  	"io"
     7  	"sync"
     8  	"time"
     9  
    10  	"github.com/Sirupsen/logrus"
    11  )
    12  
    13  // Copier can copy logs from specified sources to Logger and attach Timestamp.
    14  // Writes are concurrent, so you need implement some sync in your logger
    15  type Copier struct {
    16  	// srcs is map of name -> reader pairs, for example "stdout", "stderr"
    17  	srcs      map[string]io.Reader
    18  	dst       Logger
    19  	copyJobs  sync.WaitGroup
    20  	closeOnce sync.Once
    21  	closed    chan struct{}
    22  }
    23  
    24  // NewCopier creates a new Copier
    25  func NewCopier(srcs map[string]io.Reader, dst Logger) *Copier {
    26  	return &Copier{
    27  		srcs:   srcs,
    28  		dst:    dst,
    29  		closed: make(chan struct{}),
    30  	}
    31  }
    32  
    33  // Run starts logs copying
    34  func (c *Copier) Run() {
    35  	for src, w := range c.srcs {
    36  		c.copyJobs.Add(1)
    37  		go c.copySrc(src, w)
    38  	}
    39  }
    40  
    41  func (c *Copier) copySrc(name string, src io.Reader) {
    42  	defer c.copyJobs.Done()
    43  	reader := bufio.NewReader(src)
    44  
    45  	for {
    46  		select {
    47  		case <-c.closed:
    48  			return
    49  		default:
    50  			line, err := reader.ReadBytes('\n')
    51  			line = bytes.TrimSuffix(line, []byte{'\n'})
    52  
    53  			// ReadBytes can return full or partial output even when it failed.
    54  			// e.g. it can return a full entry and EOF.
    55  			if err == nil || len(line) > 0 {
    56  				if logErr := c.dst.Log(&Message{Line: line, Source: name, Timestamp: time.Now().UTC()}); logErr != nil {
    57  					logrus.Errorf("Failed to log msg %q for logger %s: %s", line, c.dst.Name(), logErr)
    58  				}
    59  			}
    60  
    61  			if err != nil {
    62  				if err != io.EOF {
    63  					logrus.Errorf("Error scanning log stream: %s", err)
    64  				}
    65  				return
    66  			}
    67  		}
    68  	}
    69  }
    70  
    71  // Wait waits until all copying is done
    72  func (c *Copier) Wait() {
    73  	c.copyJobs.Wait()
    74  }
    75  
    76  // Close closes the copier
    77  func (c *Copier) Close() {
    78  	c.closeOnce.Do(func() {
    79  		close(c.closed)
    80  	})
    81  }