github.com/sijibomii/docker@v0.0.0-20231230191044-5cf6ca554647/daemon/logger/jsonfilelog/read.go (about)

     1  package jsonfilelog
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"io"
     8  	"os"
     9  	"time"
    10  
    11  	"github.com/Sirupsen/logrus"
    12  	"github.com/docker/docker/daemon/logger"
    13  	"github.com/docker/docker/pkg/filenotify"
    14  	"github.com/docker/docker/pkg/ioutils"
    15  	"github.com/docker/docker/pkg/jsonlog"
    16  	"github.com/docker/docker/pkg/tailfile"
    17  )
    18  
    19  const maxJSONDecodeRetry = 20000
    20  
    21  func decodeLogLine(dec *json.Decoder, l *jsonlog.JSONLog) (*logger.Message, error) {
    22  	l.Reset()
    23  	if err := dec.Decode(l); err != nil {
    24  		return nil, err
    25  	}
    26  	msg := &logger.Message{
    27  		Source:    l.Stream,
    28  		Timestamp: l.Created,
    29  		Line:      []byte(l.Log),
    30  	}
    31  	return msg, nil
    32  }
    33  
    34  // ReadLogs implements the logger's LogReader interface for the logs
    35  // created by this driver.
    36  func (l *JSONFileLogger) ReadLogs(config logger.ReadConfig) *logger.LogWatcher {
    37  	logWatcher := logger.NewLogWatcher()
    38  
    39  	go l.readLogs(logWatcher, config)
    40  	return logWatcher
    41  }
    42  
    43  func (l *JSONFileLogger) readLogs(logWatcher *logger.LogWatcher, config logger.ReadConfig) {
    44  	defer close(logWatcher.Msg)
    45  
    46  	pth := l.writer.LogPath()
    47  	var files []io.ReadSeeker
    48  	for i := l.writer.MaxFiles(); i > 1; i-- {
    49  		f, err := os.Open(fmt.Sprintf("%s.%d", pth, i-1))
    50  		if err != nil {
    51  			if !os.IsNotExist(err) {
    52  				logWatcher.Err <- err
    53  				break
    54  			}
    55  			continue
    56  		}
    57  		files = append(files, f)
    58  	}
    59  
    60  	latestFile, err := os.Open(pth)
    61  	if err != nil {
    62  		logWatcher.Err <- err
    63  		return
    64  	}
    65  
    66  	if config.Tail != 0 {
    67  		tailer := ioutils.MultiReadSeeker(append(files, latestFile)...)
    68  		tailFile(tailer, logWatcher, config.Tail, config.Since)
    69  	}
    70  
    71  	// close all the rotated files
    72  	for _, f := range files {
    73  		if err := f.(io.Closer).Close(); err != nil {
    74  			logrus.WithField("logger", "json-file").Warnf("error closing tailed log file: %v", err)
    75  		}
    76  	}
    77  
    78  	if !config.Follow {
    79  		return
    80  	}
    81  
    82  	if config.Tail >= 0 {
    83  		latestFile.Seek(0, os.SEEK_END)
    84  	}
    85  
    86  	l.mu.Lock()
    87  	l.readers[logWatcher] = struct{}{}
    88  	l.mu.Unlock()
    89  
    90  	notifyRotate := l.writer.NotifyRotate()
    91  	followLogs(latestFile, logWatcher, notifyRotate, config.Since)
    92  
    93  	l.mu.Lock()
    94  	delete(l.readers, logWatcher)
    95  	l.mu.Unlock()
    96  
    97  	l.writer.NotifyRotateEvict(notifyRotate)
    98  }
    99  
   100  func tailFile(f io.ReadSeeker, logWatcher *logger.LogWatcher, tail int, since time.Time) {
   101  	var rdr io.Reader = f
   102  	if tail > 0 {
   103  		ls, err := tailfile.TailFile(f, tail)
   104  		if err != nil {
   105  			logWatcher.Err <- err
   106  			return
   107  		}
   108  		rdr = bytes.NewBuffer(bytes.Join(ls, []byte("\n")))
   109  	}
   110  	dec := json.NewDecoder(rdr)
   111  	l := &jsonlog.JSONLog{}
   112  	for {
   113  		msg, err := decodeLogLine(dec, l)
   114  		if err != nil {
   115  			if err != io.EOF {
   116  				logWatcher.Err <- err
   117  			}
   118  			return
   119  		}
   120  		if !since.IsZero() && msg.Timestamp.Before(since) {
   121  			continue
   122  		}
   123  		logWatcher.Msg <- msg
   124  	}
   125  }
   126  
   127  func followLogs(f *os.File, logWatcher *logger.LogWatcher, notifyRotate chan interface{}, since time.Time) {
   128  	dec := json.NewDecoder(f)
   129  	l := &jsonlog.JSONLog{}
   130  
   131  	fileWatcher, err := filenotify.New()
   132  	if err != nil {
   133  		logWatcher.Err <- err
   134  	}
   135  	defer func() {
   136  		f.Close()
   137  		fileWatcher.Close()
   138  	}()
   139  	name := f.Name()
   140  
   141  	if err := fileWatcher.Add(name); err != nil {
   142  		logrus.WithField("logger", "json-file").Warnf("falling back to file poller due to error: %v", err)
   143  		fileWatcher.Close()
   144  		fileWatcher = filenotify.NewPollingWatcher()
   145  
   146  		if err := fileWatcher.Add(name); err != nil {
   147  			logrus.Debugf("error watching log file for modifications: %v", err)
   148  			logWatcher.Err <- err
   149  			return
   150  		}
   151  	}
   152  
   153  	var retries int
   154  	for {
   155  		msg, err := decodeLogLine(dec, l)
   156  		if err != nil {
   157  			if err != io.EOF {
   158  				// try again because this shouldn't happen
   159  				if _, ok := err.(*json.SyntaxError); ok && retries <= maxJSONDecodeRetry {
   160  					dec = json.NewDecoder(f)
   161  					retries++
   162  					continue
   163  				}
   164  
   165  				// io.ErrUnexpectedEOF is returned from json.Decoder when there is
   166  				// remaining data in the parser's buffer while an io.EOF occurs.
   167  				// If the json logger writes a partial json log entry to the disk
   168  				// while at the same time the decoder tries to decode it, the race condition happens.
   169  				if err == io.ErrUnexpectedEOF && retries <= maxJSONDecodeRetry {
   170  					reader := io.MultiReader(dec.Buffered(), f)
   171  					dec = json.NewDecoder(reader)
   172  					retries++
   173  					continue
   174  				}
   175  
   176  				return
   177  			}
   178  
   179  			select {
   180  			case <-fileWatcher.Events():
   181  				dec = json.NewDecoder(f)
   182  				continue
   183  			case <-fileWatcher.Errors():
   184  				logWatcher.Err <- err
   185  				return
   186  			case <-logWatcher.WatchClose():
   187  				fileWatcher.Remove(name)
   188  				return
   189  			case <-notifyRotate:
   190  				f.Close()
   191  				fileWatcher.Remove(name)
   192  
   193  				// retry when the file doesn't exist
   194  				for retries := 0; retries <= 5; retries++ {
   195  					f, err = os.Open(name)
   196  					if err == nil || !os.IsNotExist(err) {
   197  						break
   198  					}
   199  				}
   200  
   201  				if err = fileWatcher.Add(name); err != nil {
   202  					logWatcher.Err <- err
   203  					return
   204  				}
   205  				if err != nil {
   206  					logWatcher.Err <- err
   207  					return
   208  				}
   209  
   210  				dec = json.NewDecoder(f)
   211  				continue
   212  			}
   213  		}
   214  
   215  		retries = 0 // reset retries since we've succeeded
   216  		if !since.IsZero() && msg.Timestamp.Before(since) {
   217  			continue
   218  		}
   219  		select {
   220  		case logWatcher.Msg <- msg:
   221  		case <-logWatcher.WatchClose():
   222  			logWatcher.Msg <- msg
   223  			for {
   224  				msg, err := decodeLogLine(dec, l)
   225  				if err != nil {
   226  					return
   227  				}
   228  				if !since.IsZero() && msg.Timestamp.Before(since) {
   229  					continue
   230  				}
   231  				logWatcher.Msg <- msg
   232  			}
   233  		}
   234  	}
   235  }