github.com/LazyboyChen7/engine@v17.12.1-ce-rc2+incompatible/daemon/logger/loggerutils/logfile.go (about)

     1  package loggerutils
     2  
     3  import (
     4  	"bytes"
     5  	"context"
     6  	"fmt"
     7  	"io"
     8  	"os"
     9  	"strconv"
    10  	"sync"
    11  	"time"
    12  
    13  	"github.com/docker/docker/daemon/logger"
    14  	"github.com/docker/docker/daemon/logger/loggerutils/multireader"
    15  	"github.com/docker/docker/pkg/filenotify"
    16  	"github.com/docker/docker/pkg/pubsub"
    17  	"github.com/docker/docker/pkg/tailfile"
    18  	"github.com/fsnotify/fsnotify"
    19  	"github.com/pkg/errors"
    20  	"github.com/sirupsen/logrus"
    21  )
    22  
    23  // LogFile is Logger implementation for default Docker logging.
    24  type LogFile struct {
    25  	f             *os.File // store for closing
    26  	closed        bool
    27  	mu            sync.RWMutex
    28  	capacity      int64 //maximum size of each file
    29  	currentSize   int64 // current size of the latest file
    30  	maxFiles      int   //maximum number of files
    31  	notifyRotate  *pubsub.Publisher
    32  	marshal       logger.MarshalFunc
    33  	createDecoder makeDecoderFunc
    34  }
    35  
    36  type makeDecoderFunc func(rdr io.Reader) func() (*logger.Message, error)
    37  
    38  //NewLogFile creates new LogFile
    39  func NewLogFile(logPath string, capacity int64, maxFiles int, marshaller logger.MarshalFunc, decodeFunc makeDecoderFunc) (*LogFile, error) {
    40  	log, err := os.OpenFile(logPath, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0640)
    41  	if err != nil {
    42  		return nil, err
    43  	}
    44  
    45  	size, err := log.Seek(0, os.SEEK_END)
    46  	if err != nil {
    47  		return nil, err
    48  	}
    49  
    50  	return &LogFile{
    51  		f:             log,
    52  		capacity:      capacity,
    53  		currentSize:   size,
    54  		maxFiles:      maxFiles,
    55  		notifyRotate:  pubsub.NewPublisher(0, 1),
    56  		marshal:       marshaller,
    57  		createDecoder: decodeFunc,
    58  	}, nil
    59  }
    60  
    61  // WriteLogEntry writes the provided log message to the current log file.
    62  // This may trigger a rotation event if the max file/capacity limits are hit.
    63  func (w *LogFile) WriteLogEntry(msg *logger.Message) error {
    64  	b, err := w.marshal(msg)
    65  	if err != nil {
    66  		return errors.Wrap(err, "error marshalling log message")
    67  	}
    68  
    69  	logger.PutMessage(msg)
    70  
    71  	w.mu.Lock()
    72  	if w.closed {
    73  		w.mu.Unlock()
    74  		return errors.New("cannot write because the output file was closed")
    75  	}
    76  
    77  	if err := w.checkCapacityAndRotate(); err != nil {
    78  		w.mu.Unlock()
    79  		return err
    80  	}
    81  
    82  	n, err := w.f.Write(b)
    83  	if err == nil {
    84  		w.currentSize += int64(n)
    85  	}
    86  	w.mu.Unlock()
    87  	return err
    88  }
    89  
    90  func (w *LogFile) checkCapacityAndRotate() error {
    91  	if w.capacity == -1 {
    92  		return nil
    93  	}
    94  
    95  	if w.currentSize >= w.capacity {
    96  		name := w.f.Name()
    97  		if err := w.f.Close(); err != nil {
    98  			return errors.Wrap(err, "error closing file")
    99  		}
   100  		if err := rotate(name, w.maxFiles); err != nil {
   101  			return err
   102  		}
   103  		file, err := os.OpenFile(name, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, 0640)
   104  		if err != nil {
   105  			return err
   106  		}
   107  		w.f = file
   108  		w.currentSize = 0
   109  		w.notifyRotate.Publish(struct{}{})
   110  	}
   111  
   112  	return nil
   113  }
   114  
   115  func rotate(name string, maxFiles int) error {
   116  	if maxFiles < 2 {
   117  		return nil
   118  	}
   119  	for i := maxFiles - 1; i > 1; i-- {
   120  		toPath := name + "." + strconv.Itoa(i)
   121  		fromPath := name + "." + strconv.Itoa(i-1)
   122  		if err := os.Rename(fromPath, toPath); err != nil && !os.IsNotExist(err) {
   123  			return errors.Wrap(err, "error rotating old log entries")
   124  		}
   125  	}
   126  
   127  	if err := os.Rename(name, name+".1"); err != nil && !os.IsNotExist(err) {
   128  		return errors.Wrap(err, "error rotating current log")
   129  	}
   130  	return nil
   131  }
   132  
   133  // LogPath returns the location the given writer logs to.
   134  func (w *LogFile) LogPath() string {
   135  	w.mu.Lock()
   136  	defer w.mu.Unlock()
   137  	return w.f.Name()
   138  }
   139  
   140  // MaxFiles return maximum number of files
   141  func (w *LogFile) MaxFiles() int {
   142  	return w.maxFiles
   143  }
   144  
   145  // Close closes underlying file and signals all readers to stop.
   146  func (w *LogFile) Close() error {
   147  	w.mu.Lock()
   148  	defer w.mu.Unlock()
   149  	if w.closed {
   150  		return nil
   151  	}
   152  	if err := w.f.Close(); err != nil {
   153  		return err
   154  	}
   155  	w.closed = true
   156  	return nil
   157  }
   158  
   159  // ReadLogs decodes entries from log files and sends them the passed in watcher
   160  func (w *LogFile) ReadLogs(config logger.ReadConfig, watcher *logger.LogWatcher) {
   161  	w.mu.RLock()
   162  	files, err := w.openRotatedFiles()
   163  	if err != nil {
   164  		w.mu.RUnlock()
   165  		watcher.Err <- err
   166  		return
   167  	}
   168  	defer func() {
   169  		for _, f := range files {
   170  			f.Close()
   171  		}
   172  	}()
   173  
   174  	currentFile, err := os.Open(w.f.Name())
   175  	if err != nil {
   176  		w.mu.RUnlock()
   177  		watcher.Err <- err
   178  		return
   179  	}
   180  	defer currentFile.Close()
   181  
   182  	currentChunk, err := newSectionReader(currentFile)
   183  	w.mu.RUnlock()
   184  
   185  	if err != nil {
   186  		watcher.Err <- err
   187  		return
   188  	}
   189  
   190  	if config.Tail != 0 {
   191  		seekers := make([]io.ReadSeeker, 0, len(files)+1)
   192  		for _, f := range files {
   193  			seekers = append(seekers, f)
   194  		}
   195  		seekers = append(seekers, currentChunk)
   196  		tailFile(multireader.MultiReadSeeker(seekers...), watcher, w.createDecoder, config)
   197  	}
   198  
   199  	w.mu.RLock()
   200  	if !config.Follow || w.closed {
   201  		w.mu.RUnlock()
   202  		return
   203  	}
   204  	w.mu.RUnlock()
   205  
   206  	notifyRotate := w.notifyRotate.Subscribe()
   207  	defer w.notifyRotate.Evict(notifyRotate)
   208  	followLogs(currentFile, watcher, notifyRotate, w.createDecoder, config.Since, config.Until)
   209  }
   210  
   211  func (w *LogFile) openRotatedFiles() (files []*os.File, err error) {
   212  	defer func() {
   213  		if err == nil {
   214  			return
   215  		}
   216  		for _, f := range files {
   217  			f.Close()
   218  		}
   219  	}()
   220  
   221  	for i := w.maxFiles; i > 1; i-- {
   222  		f, err := os.Open(fmt.Sprintf("%s.%d", w.f.Name(), i-1))
   223  		if err != nil {
   224  			if !os.IsNotExist(err) {
   225  				return nil, err
   226  			}
   227  			continue
   228  		}
   229  		files = append(files, f)
   230  	}
   231  
   232  	return files, nil
   233  }
   234  
   235  func newSectionReader(f *os.File) (*io.SectionReader, error) {
   236  	// seek to the end to get the size
   237  	// we'll leave this at the end of the file since section reader does not advance the reader
   238  	size, err := f.Seek(0, os.SEEK_END)
   239  	if err != nil {
   240  		return nil, errors.Wrap(err, "error getting current file size")
   241  	}
   242  	return io.NewSectionReader(f, 0, size), nil
   243  }
   244  
   245  type decodeFunc func() (*logger.Message, error)
   246  
   247  func tailFile(f io.ReadSeeker, watcher *logger.LogWatcher, createDecoder makeDecoderFunc, config logger.ReadConfig) {
   248  	var rdr io.Reader = f
   249  	if config.Tail > 0 {
   250  		ls, err := tailfile.TailFile(f, config.Tail)
   251  		if err != nil {
   252  			watcher.Err <- err
   253  			return
   254  		}
   255  		rdr = bytes.NewBuffer(bytes.Join(ls, []byte("\n")))
   256  	}
   257  
   258  	decodeLogLine := createDecoder(rdr)
   259  	for {
   260  		msg, err := decodeLogLine()
   261  		if err != nil {
   262  			if err != io.EOF {
   263  				watcher.Err <- err
   264  			}
   265  			return
   266  		}
   267  		if !config.Since.IsZero() && msg.Timestamp.Before(config.Since) {
   268  			continue
   269  		}
   270  		if !config.Until.IsZero() && msg.Timestamp.After(config.Until) {
   271  			return
   272  		}
   273  		select {
   274  		case <-watcher.WatchClose():
   275  			return
   276  		case watcher.Msg <- msg:
   277  		}
   278  	}
   279  }
   280  
   281  func followLogs(f *os.File, logWatcher *logger.LogWatcher, notifyRotate chan interface{}, createDecoder makeDecoderFunc, since, until time.Time) {
   282  	decodeLogLine := createDecoder(f)
   283  
   284  	name := f.Name()
   285  	fileWatcher, err := watchFile(name)
   286  	if err != nil {
   287  		logWatcher.Err <- err
   288  		return
   289  	}
   290  	defer func() {
   291  		f.Close()
   292  		fileWatcher.Remove(name)
   293  		fileWatcher.Close()
   294  	}()
   295  
   296  	ctx, cancel := context.WithCancel(context.Background())
   297  	defer cancel()
   298  	go func() {
   299  		select {
   300  		case <-logWatcher.WatchClose():
   301  			fileWatcher.Remove(name)
   302  			cancel()
   303  		case <-ctx.Done():
   304  			return
   305  		}
   306  	}()
   307  
   308  	var retries int
   309  	handleRotate := func() error {
   310  		f.Close()
   311  		fileWatcher.Remove(name)
   312  
   313  		// retry when the file doesn't exist
   314  		for retries := 0; retries <= 5; retries++ {
   315  			f, err = os.Open(name)
   316  			if err == nil || !os.IsNotExist(err) {
   317  				break
   318  			}
   319  		}
   320  		if err != nil {
   321  			return err
   322  		}
   323  		if err := fileWatcher.Add(name); err != nil {
   324  			return err
   325  		}
   326  		decodeLogLine = createDecoder(f)
   327  		return nil
   328  	}
   329  
   330  	errRetry := errors.New("retry")
   331  	errDone := errors.New("done")
   332  	waitRead := func() error {
   333  		select {
   334  		case e := <-fileWatcher.Events():
   335  			switch e.Op {
   336  			case fsnotify.Write:
   337  				decodeLogLine = createDecoder(f)
   338  				return nil
   339  			case fsnotify.Rename, fsnotify.Remove:
   340  				select {
   341  				case <-notifyRotate:
   342  				case <-ctx.Done():
   343  					return errDone
   344  				}
   345  				if err := handleRotate(); err != nil {
   346  					return err
   347  				}
   348  				return nil
   349  			}
   350  			return errRetry
   351  		case err := <-fileWatcher.Errors():
   352  			logrus.Debug("logger got error watching file: %v", err)
   353  			// Something happened, let's try and stay alive and create a new watcher
   354  			if retries <= 5 {
   355  				fileWatcher.Close()
   356  				fileWatcher, err = watchFile(name)
   357  				if err != nil {
   358  					return err
   359  				}
   360  				retries++
   361  				return errRetry
   362  			}
   363  			return err
   364  		case <-ctx.Done():
   365  			return errDone
   366  		}
   367  	}
   368  
   369  	handleDecodeErr := func(err error) error {
   370  		if err != io.EOF {
   371  			return err
   372  		}
   373  
   374  		for {
   375  			err := waitRead()
   376  			if err == nil {
   377  				break
   378  			}
   379  			if err == errRetry {
   380  				continue
   381  			}
   382  			return err
   383  		}
   384  		return nil
   385  	}
   386  
   387  	// main loop
   388  	for {
   389  		msg, err := decodeLogLine()
   390  		if err != nil {
   391  			if err := handleDecodeErr(err); err != nil {
   392  				if err == errDone {
   393  					return
   394  				}
   395  				// we got an unrecoverable error, so return
   396  				logWatcher.Err <- err
   397  				return
   398  			}
   399  			// ready to try again
   400  			continue
   401  		}
   402  
   403  		retries = 0 // reset retries since we've succeeded
   404  		if !since.IsZero() && msg.Timestamp.Before(since) {
   405  			continue
   406  		}
   407  		if !until.IsZero() && msg.Timestamp.After(until) {
   408  			return
   409  		}
   410  		select {
   411  		case logWatcher.Msg <- msg:
   412  		case <-ctx.Done():
   413  			logWatcher.Msg <- msg
   414  			for {
   415  				msg, err := decodeLogLine()
   416  				if err != nil {
   417  					return
   418  				}
   419  				if !since.IsZero() && msg.Timestamp.Before(since) {
   420  					continue
   421  				}
   422  				if !until.IsZero() && msg.Timestamp.After(until) {
   423  					return
   424  				}
   425  				logWatcher.Msg <- msg
   426  			}
   427  		}
   428  	}
   429  }
   430  
   431  func watchFile(name string) (filenotify.FileWatcher, error) {
   432  	fileWatcher, err := filenotify.New()
   433  	if err != nil {
   434  		return nil, err
   435  	}
   436  
   437  	logger := logrus.WithFields(logrus.Fields{
   438  		"module": "logger",
   439  		"fille":  name,
   440  	})
   441  
   442  	if err := fileWatcher.Add(name); err != nil {
   443  		logger.WithError(err).Warnf("falling back to file poller")
   444  		fileWatcher.Close()
   445  		fileWatcher = filenotify.NewPollingWatcher()
   446  
   447  		if err := fileWatcher.Add(name); err != nil {
   448  			fileWatcher.Close()
   449  			logger.WithError(err).Debugf("error watching log file for modifications")
   450  			return nil, err
   451  		}
   452  	}
   453  	return fileWatcher, nil
   454  }