github.com/docker/docker@v299999999.0.0-20200612211812-aaf470eca7b5+incompatible/daemon/logger/ring.go (about)

     1  package logger // import "github.com/docker/docker/daemon/logger"
     2  
     3  import (
     4  	"errors"
     5  	"sync"
     6  	"sync/atomic"
     7  
     8  	"github.com/sirupsen/logrus"
     9  )
    10  
    11  const (
    12  	defaultRingMaxSize = 1e6 // 1MB
    13  )
    14  
    15  // RingLogger is a ring buffer that implements the Logger interface.
    16  // This is used when lossy logging is OK.
    17  type RingLogger struct {
    18  	buffer    *messageRing
    19  	l         Logger
    20  	logInfo   Info
    21  	closeFlag int32
    22  }
    23  
    24  type ringWithReader struct {
    25  	*RingLogger
    26  }
    27  
    28  func (r *ringWithReader) ReadLogs(cfg ReadConfig) *LogWatcher {
    29  	reader, ok := r.l.(LogReader)
    30  	if !ok {
    31  		// something is wrong if we get here
    32  		panic("expected log reader")
    33  	}
    34  	return reader.ReadLogs(cfg)
    35  }
    36  
    37  func newRingLogger(driver Logger, logInfo Info, maxSize int64) *RingLogger {
    38  	l := &RingLogger{
    39  		buffer:  newRing(maxSize),
    40  		l:       driver,
    41  		logInfo: logInfo,
    42  	}
    43  	go l.run()
    44  	return l
    45  }
    46  
    47  // NewRingLogger creates a new Logger that is implemented as a RingBuffer wrapping
    48  // the passed in logger.
    49  func NewRingLogger(driver Logger, logInfo Info, maxSize int64) Logger {
    50  	if maxSize < 0 {
    51  		maxSize = defaultRingMaxSize
    52  	}
    53  	l := newRingLogger(driver, logInfo, maxSize)
    54  	if _, ok := driver.(LogReader); ok {
    55  		return &ringWithReader{l}
    56  	}
    57  	return l
    58  }
    59  
    60  // Log queues messages into the ring buffer
    61  func (r *RingLogger) Log(msg *Message) error {
    62  	if r.closed() {
    63  		return errClosed
    64  	}
    65  	return r.buffer.Enqueue(msg)
    66  }
    67  
    68  // Name returns the name of the underlying logger
    69  func (r *RingLogger) Name() string {
    70  	return r.l.Name()
    71  }
    72  
    73  func (r *RingLogger) closed() bool {
    74  	return atomic.LoadInt32(&r.closeFlag) == 1
    75  }
    76  
    77  func (r *RingLogger) setClosed() {
    78  	atomic.StoreInt32(&r.closeFlag, 1)
    79  }
    80  
    81  // Close closes the logger
    82  func (r *RingLogger) Close() error {
    83  	r.setClosed()
    84  	r.buffer.Close()
    85  	// empty out the queue
    86  	var logErr bool
    87  	for _, msg := range r.buffer.Drain() {
    88  		if logErr {
    89  			// some error logging a previous message, so re-insert to message pool
    90  			// and assume log driver is hosed
    91  			PutMessage(msg)
    92  			continue
    93  		}
    94  
    95  		if err := r.l.Log(msg); err != nil {
    96  			logrus.WithField("driver", r.l.Name()).
    97  				WithField("container", r.logInfo.ContainerID).
    98  				WithError(err).
    99  				Errorf("Error writing log message")
   100  			logErr = true
   101  		}
   102  	}
   103  	return r.l.Close()
   104  }
   105  
   106  // run consumes messages from the ring buffer and forwards them to the underling
   107  // logger.
   108  // This is run in a goroutine when the RingLogger is created
   109  func (r *RingLogger) run() {
   110  	for {
   111  		if r.closed() {
   112  			return
   113  		}
   114  		msg, err := r.buffer.Dequeue()
   115  		if err != nil {
   116  			// buffer is closed
   117  			return
   118  		}
   119  		if err := r.l.Log(msg); err != nil {
   120  			logrus.WithField("driver", r.l.Name()).
   121  				WithField("container", r.logInfo.ContainerID).
   122  				WithError(err).
   123  				Errorf("Error writing log message")
   124  		}
   125  	}
   126  }
   127  
   128  type messageRing struct {
   129  	mu sync.Mutex
   130  	// signals callers of `Dequeue` to wake up either on `Close` or when a new `Message` is added
   131  	wait *sync.Cond
   132  
   133  	sizeBytes int64 // current buffer size
   134  	maxBytes  int64 // max buffer size size
   135  	queue     []*Message
   136  	closed    bool
   137  }
   138  
   139  func newRing(maxBytes int64) *messageRing {
   140  	queueSize := 1000
   141  	if maxBytes == 0 || maxBytes == 1 {
   142  		// With 0 or 1 max byte size, the maximum size of the queue would only ever be 1
   143  		// message long.
   144  		queueSize = 1
   145  	}
   146  
   147  	r := &messageRing{queue: make([]*Message, 0, queueSize), maxBytes: maxBytes}
   148  	r.wait = sync.NewCond(&r.mu)
   149  	return r
   150  }
   151  
   152  // Enqueue adds a message to the buffer queue
   153  // If the message is too big for the buffer it drops the new message.
   154  // If there are no messages in the queue and the message is still too big, it adds the message anyway.
   155  func (r *messageRing) Enqueue(m *Message) error {
   156  	mSize := int64(len(m.Line))
   157  
   158  	r.mu.Lock()
   159  	if r.closed {
   160  		r.mu.Unlock()
   161  		return errClosed
   162  	}
   163  	if mSize+r.sizeBytes > r.maxBytes && len(r.queue) > 0 {
   164  		r.wait.Signal()
   165  		r.mu.Unlock()
   166  		return nil
   167  	}
   168  
   169  	r.queue = append(r.queue, m)
   170  	r.sizeBytes += mSize
   171  	r.wait.Signal()
   172  	r.mu.Unlock()
   173  	return nil
   174  }
   175  
   176  // Dequeue pulls a message off the queue
   177  // If there are no messages, it waits for one.
   178  // If the buffer is closed, it will return immediately.
   179  func (r *messageRing) Dequeue() (*Message, error) {
   180  	r.mu.Lock()
   181  	for len(r.queue) == 0 && !r.closed {
   182  		r.wait.Wait()
   183  	}
   184  
   185  	if r.closed {
   186  		r.mu.Unlock()
   187  		return nil, errClosed
   188  	}
   189  
   190  	msg := r.queue[0]
   191  	r.queue = r.queue[1:]
   192  	r.sizeBytes -= int64(len(msg.Line))
   193  	r.mu.Unlock()
   194  	return msg, nil
   195  }
   196  
   197  var errClosed = errors.New("closed")
   198  
   199  // Close closes the buffer ensuring no new messages can be added.
   200  // Any callers waiting to dequeue a message will be woken up.
   201  func (r *messageRing) Close() {
   202  	r.mu.Lock()
   203  	if r.closed {
   204  		r.mu.Unlock()
   205  		return
   206  	}
   207  
   208  	r.closed = true
   209  	r.wait.Broadcast()
   210  	r.mu.Unlock()
   211  }
   212  
   213  // Drain drains all messages from the queue.
   214  // This can be used after `Close()` to get any remaining messages that were in queue.
   215  func (r *messageRing) Drain() []*Message {
   216  	r.mu.Lock()
   217  	ls := make([]*Message, 0, len(r.queue))
   218  	ls = append(ls, r.queue...)
   219  	r.sizeBytes = 0
   220  	r.queue = r.queue[:0]
   221  	r.mu.Unlock()
   222  	return ls
   223  }