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