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