gopkg.in/dotcloud/docker.v1@v1.13.1/daemon/logger/jsonfilelog/read.go (about)

     1  package jsonfilelog
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"errors"
     7  	"fmt"
     8  	"io"
     9  	"os"
    10  	"time"
    11  
    12  	"github.com/fsnotify/fsnotify"
    13  	"golang.org/x/net/context"
    14  
    15  	"github.com/Sirupsen/logrus"
    16  	"github.com/docker/docker/daemon/logger"
    17  	"github.com/docker/docker/pkg/filenotify"
    18  	"github.com/docker/docker/pkg/ioutils"
    19  	"github.com/docker/docker/pkg/jsonlog"
    20  	"github.com/docker/docker/pkg/tailfile"
    21  )
    22  
    23  const maxJSONDecodeRetry = 20000
    24  
    25  func decodeLogLine(dec *json.Decoder, l *jsonlog.JSONLog) (*logger.Message, error) {
    26  	l.Reset()
    27  	if err := dec.Decode(l); err != nil {
    28  		return nil, err
    29  	}
    30  	msg := &logger.Message{
    31  		Source:    l.Stream,
    32  		Timestamp: l.Created,
    33  		Line:      []byte(l.Log),
    34  		Attrs:     l.Attrs,
    35  	}
    36  	return msg, nil
    37  }
    38  
    39  // ReadLogs implements the logger's LogReader interface for the logs
    40  // created by this driver.
    41  func (l *JSONFileLogger) ReadLogs(config logger.ReadConfig) *logger.LogWatcher {
    42  	logWatcher := logger.NewLogWatcher()
    43  
    44  	go l.readLogs(logWatcher, config)
    45  	return logWatcher
    46  }
    47  
    48  func (l *JSONFileLogger) readLogs(logWatcher *logger.LogWatcher, config logger.ReadConfig) {
    49  	defer close(logWatcher.Msg)
    50  
    51  	// lock so the read stream doesn't get corrupted due to rotations or other log data written while we read
    52  	// This will block writes!!!
    53  	l.mu.Lock()
    54  
    55  	pth := l.writer.LogPath()
    56  	var files []io.ReadSeeker
    57  	for i := l.writer.MaxFiles(); i > 1; i-- {
    58  		f, err := os.Open(fmt.Sprintf("%s.%d", pth, i-1))
    59  		if err != nil {
    60  			if !os.IsNotExist(err) {
    61  				logWatcher.Err <- err
    62  				break
    63  			}
    64  			continue
    65  		}
    66  		defer f.Close()
    67  
    68  		files = append(files, f)
    69  	}
    70  
    71  	latestFile, err := os.Open(pth)
    72  	if err != nil {
    73  		logWatcher.Err <- err
    74  		l.mu.Unlock()
    75  		return
    76  	}
    77  	defer latestFile.Close()
    78  
    79  	if config.Tail != 0 {
    80  		tailer := ioutils.MultiReadSeeker(append(files, latestFile)...)
    81  		tailFile(tailer, logWatcher, config.Tail, config.Since)
    82  	}
    83  
    84  	// close all the rotated files
    85  	for _, f := range files {
    86  		if err := f.(io.Closer).Close(); err != nil {
    87  			logrus.WithField("logger", "json-file").Warnf("error closing tailed log file: %v", err)
    88  		}
    89  	}
    90  
    91  	if !config.Follow {
    92  		if err := latestFile.Close(); err != nil {
    93  			logrus.Errorf("Error closing file: %v", err)
    94  		}
    95  		l.mu.Unlock()
    96  		return
    97  	}
    98  
    99  	if config.Tail >= 0 {
   100  		latestFile.Seek(0, os.SEEK_END)
   101  	}
   102  
   103  	l.readers[logWatcher] = struct{}{}
   104  	l.mu.Unlock()
   105  
   106  	notifyRotate := l.writer.NotifyRotate()
   107  	followLogs(latestFile, logWatcher, notifyRotate, config.Since)
   108  
   109  	l.mu.Lock()
   110  	delete(l.readers, logWatcher)
   111  	l.mu.Unlock()
   112  
   113  	l.writer.NotifyRotateEvict(notifyRotate)
   114  }
   115  
   116  func tailFile(f io.ReadSeeker, logWatcher *logger.LogWatcher, tail int, since time.Time) {
   117  	var rdr io.Reader
   118  	rdr = f
   119  	if tail > 0 {
   120  		ls, err := tailfile.TailFile(f, tail)
   121  		if err != nil {
   122  			logWatcher.Err <- err
   123  			return
   124  		}
   125  		rdr = bytes.NewBuffer(bytes.Join(ls, []byte("\n")))
   126  	}
   127  	dec := json.NewDecoder(rdr)
   128  	l := &jsonlog.JSONLog{}
   129  	for {
   130  		msg, err := decodeLogLine(dec, l)
   131  		if err != nil {
   132  			if err != io.EOF {
   133  				logWatcher.Err <- err
   134  			}
   135  			return
   136  		}
   137  		if !since.IsZero() && msg.Timestamp.Before(since) {
   138  			continue
   139  		}
   140  		logWatcher.Msg <- msg
   141  	}
   142  }
   143  
   144  func watchFile(name string) (filenotify.FileWatcher, error) {
   145  	fileWatcher, err := filenotify.New()
   146  	if err != nil {
   147  		return nil, err
   148  	}
   149  
   150  	if err := fileWatcher.Add(name); err != nil {
   151  		logrus.WithField("logger", "json-file").Warnf("falling back to file poller due to error: %v", err)
   152  		fileWatcher.Close()
   153  		fileWatcher = filenotify.NewPollingWatcher()
   154  
   155  		if err := fileWatcher.Add(name); err != nil {
   156  			fileWatcher.Close()
   157  			logrus.Debugf("error watching log file for modifications: %v", err)
   158  			return nil, err
   159  		}
   160  	}
   161  	return fileWatcher, nil
   162  }
   163  
   164  func followLogs(f *os.File, logWatcher *logger.LogWatcher, notifyRotate chan interface{}, since time.Time) {
   165  	dec := json.NewDecoder(f)
   166  	l := &jsonlog.JSONLog{}
   167  
   168  	name := f.Name()
   169  	fileWatcher, err := watchFile(name)
   170  	if err != nil {
   171  		logWatcher.Err <- err
   172  		return
   173  	}
   174  	defer func() {
   175  		f.Close()
   176  		fileWatcher.Remove(name)
   177  		fileWatcher.Close()
   178  	}()
   179  
   180  	ctx, cancel := context.WithCancel(context.Background())
   181  	defer cancel()
   182  	go func() {
   183  		select {
   184  		case <-logWatcher.WatchClose():
   185  			fileWatcher.Remove(name)
   186  			cancel()
   187  		case <-ctx.Done():
   188  			return
   189  		}
   190  	}()
   191  
   192  	var retries int
   193  	handleRotate := func() error {
   194  		f.Close()
   195  		fileWatcher.Remove(name)
   196  
   197  		// retry when the file doesn't exist
   198  		for retries := 0; retries <= 5; retries++ {
   199  			f, err = os.Open(name)
   200  			if err == nil || !os.IsNotExist(err) {
   201  				break
   202  			}
   203  		}
   204  		if err != nil {
   205  			return err
   206  		}
   207  		if err := fileWatcher.Add(name); err != nil {
   208  			return err
   209  		}
   210  		dec = json.NewDecoder(f)
   211  		return nil
   212  	}
   213  
   214  	errRetry := errors.New("retry")
   215  	errDone := errors.New("done")
   216  	waitRead := func() error {
   217  		select {
   218  		case e := <-fileWatcher.Events():
   219  			switch e.Op {
   220  			case fsnotify.Write:
   221  				dec = json.NewDecoder(f)
   222  				return nil
   223  			case fsnotify.Rename, fsnotify.Remove:
   224  				select {
   225  				case <-notifyRotate:
   226  				case <-ctx.Done():
   227  					return errDone
   228  				}
   229  				if err := handleRotate(); err != nil {
   230  					return err
   231  				}
   232  				return nil
   233  			}
   234  			return errRetry
   235  		case err := <-fileWatcher.Errors():
   236  			logrus.Debug("logger got error watching file: %v", err)
   237  			// Something happened, let's try and stay alive and create a new watcher
   238  			if retries <= 5 {
   239  				fileWatcher.Close()
   240  				fileWatcher, err = watchFile(name)
   241  				if err != nil {
   242  					return err
   243  				}
   244  				retries++
   245  				return errRetry
   246  			}
   247  			return err
   248  		case <-ctx.Done():
   249  			return errDone
   250  		}
   251  	}
   252  
   253  	handleDecodeErr := func(err error) error {
   254  		if err == io.EOF {
   255  			for err := waitRead(); err != nil; {
   256  				if err == errRetry {
   257  					// retry the waitRead
   258  					continue
   259  				}
   260  				return err
   261  			}
   262  			return nil
   263  		}
   264  		// try again because this shouldn't happen
   265  		if _, ok := err.(*json.SyntaxError); ok && retries <= maxJSONDecodeRetry {
   266  			dec = json.NewDecoder(f)
   267  			retries++
   268  			return nil
   269  		}
   270  		// io.ErrUnexpectedEOF is returned from json.Decoder when there is
   271  		// remaining data in the parser's buffer while an io.EOF occurs.
   272  		// If the json logger writes a partial json log entry to the disk
   273  		// while at the same time the decoder tries to decode it, the race condition happens.
   274  		if err == io.ErrUnexpectedEOF && retries <= maxJSONDecodeRetry {
   275  			reader := io.MultiReader(dec.Buffered(), f)
   276  			dec = json.NewDecoder(reader)
   277  			retries++
   278  			return nil
   279  		}
   280  		return err
   281  	}
   282  
   283  	// main loop
   284  	for {
   285  		msg, err := decodeLogLine(dec, l)
   286  		if err != nil {
   287  			if err := handleDecodeErr(err); err != nil {
   288  				if err == errDone {
   289  					return
   290  				}
   291  				// we got an unrecoverable error, so return
   292  				logWatcher.Err <- err
   293  				return
   294  			}
   295  			// ready to try again
   296  			continue
   297  		}
   298  
   299  		retries = 0 // reset retries since we've succeeded
   300  		if !since.IsZero() && msg.Timestamp.Before(since) {
   301  			continue
   302  		}
   303  		select {
   304  		case logWatcher.Msg <- msg:
   305  		case <-ctx.Done():
   306  			logWatcher.Msg <- msg
   307  			for {
   308  				msg, err := decodeLogLine(dec, l)
   309  				if err != nil {
   310  					return
   311  				}
   312  				if !since.IsZero() && msg.Timestamp.Before(since) {
   313  					continue
   314  				}
   315  				logWatcher.Msg <- msg
   316  			}
   317  		}
   318  	}
   319  }