github.com/ssdev-go/moby@v17.12.1-ce-rc2+incompatible/daemon/logger/ring.go (about)

     1  package 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()).WithField("container", r.logInfo.ContainerID).Errorf("Error writing log message: %v", r.l)
    97  			logErr = true
    98  		}
    99  	}
   100  	return r.l.Close()
   101  }
   102  
   103  // run consumes messages from the ring buffer and forwards them to the underling
   104  // logger.
   105  // This is run in a goroutine when the RingLogger is created
   106  func (r *RingLogger) run() {
   107  	for {
   108  		if r.closed() {
   109  			return
   110  		}
   111  		msg, err := r.buffer.Dequeue()
   112  		if err != nil {
   113  			// buffer is closed
   114  			return
   115  		}
   116  		if err := r.l.Log(msg); err != nil {
   117  			logrus.WithField("driver", r.l.Name()).WithField("container", r.logInfo.ContainerID).Errorf("Error writing log message: %v", r.l)
   118  		}
   119  	}
   120  }
   121  
   122  type messageRing struct {
   123  	mu sync.Mutex
   124  	// signals callers of `Dequeue` to wake up either on `Close` or when a new `Message` is added
   125  	wait *sync.Cond
   126  
   127  	sizeBytes int64 // current buffer size
   128  	maxBytes  int64 // max buffer size size
   129  	queue     []*Message
   130  	closed    bool
   131  }
   132  
   133  func newRing(maxBytes int64) *messageRing {
   134  	queueSize := 1000
   135  	if maxBytes == 0 || maxBytes == 1 {
   136  		// With 0 or 1 max byte size, the maximum size of the queue would only ever be 1
   137  		// message long.
   138  		queueSize = 1
   139  	}
   140  
   141  	r := &messageRing{queue: make([]*Message, 0, queueSize), maxBytes: maxBytes}
   142  	r.wait = sync.NewCond(&r.mu)
   143  	return r
   144  }
   145  
   146  // Enqueue adds a message to the buffer queue
   147  // If the message is too big for the buffer it drops the oldest messages to make room
   148  // If there are no messages in the queue and the message is still too big, it adds the message anyway.
   149  func (r *messageRing) Enqueue(m *Message) error {
   150  	mSize := int64(len(m.Line))
   151  
   152  	r.mu.Lock()
   153  	if r.closed {
   154  		r.mu.Unlock()
   155  		return errClosed
   156  	}
   157  	if mSize+r.sizeBytes > r.maxBytes && len(r.queue) > 0 {
   158  		r.wait.Signal()
   159  		r.mu.Unlock()
   160  		return nil
   161  	}
   162  
   163  	r.queue = append(r.queue, m)
   164  	r.sizeBytes += mSize
   165  	r.wait.Signal()
   166  	r.mu.Unlock()
   167  	return nil
   168  }
   169  
   170  // Dequeue pulls a message off the queue
   171  // If there are no messages, it waits for one.
   172  // If the buffer is closed, it will return immediately.
   173  func (r *messageRing) Dequeue() (*Message, error) {
   174  	r.mu.Lock()
   175  	for len(r.queue) == 0 && !r.closed {
   176  		r.wait.Wait()
   177  	}
   178  
   179  	if r.closed {
   180  		r.mu.Unlock()
   181  		return nil, errClosed
   182  	}
   183  
   184  	msg := r.queue[0]
   185  	r.queue = r.queue[1:]
   186  	r.sizeBytes -= int64(len(msg.Line))
   187  	r.mu.Unlock()
   188  	return msg, nil
   189  }
   190  
   191  var errClosed = errors.New("closed")
   192  
   193  // Close closes the buffer ensuring no new messages can be added.
   194  // Any callers waiting to dequeue a message will be woken up.
   195  func (r *messageRing) Close() {
   196  	r.mu.Lock()
   197  	if r.closed {
   198  		r.mu.Unlock()
   199  		return
   200  	}
   201  
   202  	r.closed = true
   203  	r.wait.Broadcast()
   204  	r.mu.Unlock()
   205  }
   206  
   207  // Drain drains all messages from the queue.
   208  // This can be used after `Close()` to get any remaining messages that were in queue.
   209  func (r *messageRing) Drain() []*Message {
   210  	r.mu.Lock()
   211  	ls := make([]*Message, 0, len(r.queue))
   212  	ls = append(ls, r.queue...)
   213  	r.sizeBytes = 0
   214  	r.queue = r.queue[:0]
   215  	r.mu.Unlock()
   216  	return ls
   217  }