github.com/devdivbcp/moby@v17.12.0-ce-rc1.0.20200726071732-2d4bfdc789ad+incompatible/daemon/logger/jsonfilelog/read.go (about)

     1  package jsonfilelog // import "github.com/docker/docker/daemon/logger/jsonfilelog"
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"io"
     7  
     8  	"github.com/docker/docker/api/types/backend"
     9  	"github.com/docker/docker/daemon/logger"
    10  	"github.com/docker/docker/daemon/logger/jsonfilelog/jsonlog"
    11  	"github.com/docker/docker/daemon/logger/loggerutils"
    12  	"github.com/docker/docker/pkg/tailfile"
    13  	"github.com/sirupsen/logrus"
    14  )
    15  
    16  const maxJSONDecodeRetry = 20000
    17  
    18  // ReadLogs implements the logger's LogReader interface for the logs
    19  // created by this driver.
    20  func (l *JSONFileLogger) ReadLogs(config logger.ReadConfig) *logger.LogWatcher {
    21  	logWatcher := logger.NewLogWatcher()
    22  
    23  	go l.readLogs(logWatcher, config)
    24  	return logWatcher
    25  }
    26  
    27  func (l *JSONFileLogger) readLogs(watcher *logger.LogWatcher, config logger.ReadConfig) {
    28  	defer close(watcher.Msg)
    29  
    30  	l.mu.Lock()
    31  	l.readers[watcher] = struct{}{}
    32  	l.mu.Unlock()
    33  
    34  	l.writer.ReadLogs(config, watcher)
    35  
    36  	l.mu.Lock()
    37  	delete(l.readers, watcher)
    38  	l.mu.Unlock()
    39  }
    40  
    41  func decodeLogLine(dec *json.Decoder, l *jsonlog.JSONLog) (*logger.Message, error) {
    42  	l.Reset()
    43  	if err := dec.Decode(l); err != nil {
    44  		return nil, err
    45  	}
    46  
    47  	var attrs []backend.LogAttr
    48  	if len(l.Attrs) != 0 {
    49  		attrs = make([]backend.LogAttr, 0, len(l.Attrs))
    50  		for k, v := range l.Attrs {
    51  			attrs = append(attrs, backend.LogAttr{Key: k, Value: v})
    52  		}
    53  	}
    54  	msg := &logger.Message{
    55  		Source:    l.Stream,
    56  		Timestamp: l.Created,
    57  		Line:      []byte(l.Log),
    58  		Attrs:     attrs,
    59  	}
    60  	return msg, nil
    61  }
    62  
    63  // decodeFunc is used to create a decoder for the log file reader
    64  func decodeFunc(rdr io.Reader) func() (*logger.Message, error) {
    65  	l := &jsonlog.JSONLog{}
    66  	dec := json.NewDecoder(rdr)
    67  	return func() (msg *logger.Message, err error) {
    68  		for retries := 0; retries < maxJSONDecodeRetry; retries++ {
    69  			msg, err = decodeLogLine(dec, l)
    70  			if err == nil || err == io.EOF {
    71  				break
    72  			}
    73  
    74  			logrus.WithError(err).WithField("retries", retries).Warn("got error while decoding json")
    75  			// try again, could be due to a an incomplete json object as we read
    76  			if _, ok := err.(*json.SyntaxError); ok {
    77  				dec = json.NewDecoder(rdr)
    78  				continue
    79  			}
    80  
    81  			// io.ErrUnexpectedEOF is returned from json.Decoder when there is
    82  			// remaining data in the parser's buffer while an io.EOF occurs.
    83  			// If the json logger writes a partial json log entry to the disk
    84  			// while at the same time the decoder tries to decode it, the race condition happens.
    85  			if err == io.ErrUnexpectedEOF {
    86  				reader := io.MultiReader(dec.Buffered(), rdr)
    87  				dec = json.NewDecoder(reader)
    88  				continue
    89  			}
    90  		}
    91  		return msg, err
    92  	}
    93  }
    94  
    95  func getTailReader(ctx context.Context, r loggerutils.SizeReaderAt, req int) (io.Reader, int, error) {
    96  	return tailfile.NewTailReader(ctx, r, req)
    97  }