github.com/Prakhar-Agarwal-byte/moby@v0.0.0-20231027092010-a14e3e8ab87e/daemon/logger/loggerutils/follow.go (about)

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