github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+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  type decoder struct {
    64  	rdr io.Reader
    65  	dec *json.Decoder
    66  	jl  *jsonlog.JSONLog
    67  }
    68  
    69  func (d *decoder) Reset(rdr io.Reader) {
    70  	d.rdr = rdr
    71  	d.dec = nil
    72  	if d.jl != nil {
    73  		d.jl.Reset()
    74  	}
    75  }
    76  
    77  func (d *decoder) Close() {
    78  	d.dec = nil
    79  	d.rdr = nil
    80  	d.jl = nil
    81  }
    82  
    83  func (d *decoder) Decode() (msg *logger.Message, err error) {
    84  	if d.dec == nil {
    85  		d.dec = json.NewDecoder(d.rdr)
    86  	}
    87  	if d.jl == nil {
    88  		d.jl = &jsonlog.JSONLog{}
    89  	}
    90  	for retries := 0; retries < maxJSONDecodeRetry; retries++ {
    91  		msg, err = decodeLogLine(d.dec, d.jl)
    92  		if err == nil || err == io.EOF {
    93  			break
    94  		}
    95  
    96  		logrus.WithError(err).WithField("retries", retries).Warn("got error while decoding json")
    97  		// try again, could be due to a an incomplete json object as we read
    98  		if _, ok := err.(*json.SyntaxError); ok {
    99  			d.dec = json.NewDecoder(d.rdr)
   100  			continue
   101  		}
   102  
   103  		// io.ErrUnexpectedEOF is returned from json.Decoder when there is
   104  		// remaining data in the parser's buffer while an io.EOF occurs.
   105  		// If the json logger writes a partial json log entry to the disk
   106  		// while at the same time the decoder tries to decode it, the race condition happens.
   107  		if err == io.ErrUnexpectedEOF {
   108  			d.rdr = io.MultiReader(d.dec.Buffered(), d.rdr)
   109  			d.dec = json.NewDecoder(d.rdr)
   110  			continue
   111  		}
   112  	}
   113  	return msg, err
   114  }
   115  
   116  // decodeFunc is used to create a decoder for the log file reader
   117  func decodeFunc(rdr io.Reader) loggerutils.Decoder {
   118  	return &decoder{
   119  		rdr: rdr,
   120  		dec: nil,
   121  		jl:  nil,
   122  	}
   123  }
   124  
   125  func getTailReader(ctx context.Context, r loggerutils.SizeReaderAt, req int) (io.Reader, int, error) {
   126  	return tailfile.NewTailReader(ctx, r, req)
   127  }