github.com/Heebron/moby@v0.0.0-20221111184709-6eab4f55faf7/daemon/logger/loggerutils/follow.go (about)

     1  package loggerutils // import "github.com/docker/docker/daemon/logger/loggerutils"
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"os"
     7  
     8  	"github.com/docker/docker/daemon/logger"
     9  	"github.com/pkg/errors"
    10  	"github.com/sirupsen/logrus"
    11  )
    12  
    13  type follow struct {
    14  	LogFile   *LogFile
    15  	Watcher   *logger.LogWatcher
    16  	Decoder   Decoder
    17  	Forwarder *forwarder
    18  
    19  	log *logrus.Entry
    20  	c   chan logPos
    21  }
    22  
    23  // Do follows the log file as it is written, starting from f at read.
    24  func (fl *follow) Do(f *os.File, read logPos) {
    25  	fl.log = logrus.WithFields(logrus.Fields{
    26  		"module": "logger",
    27  		"file":   f.Name(),
    28  	})
    29  	// Optimization: allocate the write-notifications channel only once and
    30  	// reuse it for multiple invocations of nextPos().
    31  	fl.c = make(chan logPos, 1)
    32  
    33  	defer func() {
    34  		if err := f.Close(); err != nil && !errors.Is(err, os.ErrClosed) {
    35  			fl.log.WithError(err).Warn("error closing current log file")
    36  		}
    37  	}()
    38  
    39  	for {
    40  		wrote, ok := fl.nextPos(read)
    41  		if !ok {
    42  			return
    43  		}
    44  
    45  		if wrote.rotation != read.rotation {
    46  			// Flush the current file before moving on to the next.
    47  			if _, err := f.Seek(read.size, io.SeekStart); err != nil {
    48  				fl.Watcher.Err <- err
    49  				return
    50  			}
    51  			if !fl.forward(f) {
    52  				return
    53  			}
    54  
    55  			// Open the new file, which has the same name as the old
    56  			// file thanks to file rotation. Make no mistake: they
    57  			// are different files, with distinct identities.
    58  			// Atomically capture the wrote position to make
    59  			// absolutely sure that the position corresponds to the
    60  			// file we have opened; more rotations could have
    61  			// occurred since we previously received it.
    62  			if err := f.Close(); err != nil {
    63  				fl.log.WithError(err).Warn("error closing rotated log file")
    64  			}
    65  			var err error
    66  			func() {
    67  				fl.LogFile.fsopMu.RLock()
    68  				st := <-fl.LogFile.read
    69  				defer func() {
    70  					fl.LogFile.read <- st
    71  					fl.LogFile.fsopMu.RUnlock()
    72  				}()
    73  				f, err = open(f.Name())
    74  				wrote = st.pos
    75  			}()
    76  			// We tried to open the file inside a critical section
    77  			// so we shouldn't have been racing the rotation of the
    78  			// file. Any error, even fs.ErrNotFound, is exceptional.
    79  			if err != nil {
    80  				fl.Watcher.Err <- fmt.Errorf("logger: error opening log file for follow after rotation: %w", err)
    81  				return
    82  			}
    83  
    84  			if nrot := wrote.rotation - read.rotation; nrot > 1 {
    85  				fl.log.WithField("missed-rotations", nrot).
    86  					Warn("file rotations were missed while following logs; some log messages have been skipped over")
    87  			}
    88  
    89  			// Set up our read position to start from the top of the file.
    90  			read.size = 0
    91  		}
    92  
    93  		if !fl.forward(io.NewSectionReader(f, read.size, wrote.size-read.size)) {
    94  			return
    95  		}
    96  		read = wrote
    97  	}
    98  }
    99  
   100  // nextPos waits until the write position of the LogFile being followed has
   101  // advanced from current and returns the new position.
   102  func (fl *follow) nextPos(current logPos) (next logPos, ok bool) {
   103  	var st logReadState
   104  	select {
   105  	case <-fl.Watcher.WatchConsumerGone():
   106  		return current, false
   107  	case st = <-fl.LogFile.read:
   108  	}
   109  
   110  	// Have any any logs been written since we last checked?
   111  	if st.pos == current { // Nope.
   112  		// Add ourself to the notify list.
   113  		st.wait = append(st.wait, fl.c)
   114  	} else { // Yes.
   115  		// "Notify" ourself immediately.
   116  		fl.c <- st.pos
   117  	}
   118  	fl.LogFile.read <- st
   119  
   120  	select {
   121  	case <-fl.LogFile.closed: // No more logs will be written.
   122  		select { // Have we followed to the end?
   123  		case next = <-fl.c: // No: received a new position.
   124  		default: // Yes.
   125  			return current, false
   126  		}
   127  	case <-fl.Watcher.WatchConsumerGone():
   128  		return current, false
   129  	case next = <-fl.c:
   130  	}
   131  	return next, true
   132  }
   133  
   134  // forward decodes log messages from r and forwards them to the log watcher.
   135  //
   136  // The return value, cont, signals whether following should continue.
   137  func (fl *follow) forward(r io.Reader) (cont bool) {
   138  	fl.Decoder.Reset(r)
   139  	return fl.Forwarder.Do(fl.Watcher, fl.Decoder)
   140  }