github.com/kaisenlinux/docker.io@v0.0.0-20230510090727-ea55db55fac7/engine/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  var _ SizedLogger = &RingLogger{}
    25  
    26  type ringWithReader struct {
    27  	*RingLogger
    28  }
    29  
    30  func (r *ringWithReader) ReadLogs(cfg ReadConfig) *LogWatcher {
    31  	reader, ok := r.l.(LogReader)
    32  	if !ok {
    33  		// something is wrong if we get here
    34  		panic("expected log reader")
    35  	}
    36  	return reader.ReadLogs(cfg)
    37  }
    38  
    39  func newRingLogger(driver Logger, logInfo Info, maxSize int64) *RingLogger {
    40  	l := &RingLogger{
    41  		buffer:  newRing(maxSize),
    42  		l:       driver,
    43  		logInfo: logInfo,
    44  	}
    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  	// empty out the queue
    97  	var logErr bool
    98  	for _, msg := range r.buffer.Drain() {
    99  		if logErr {
   100  			// some error logging a previous message, so re-insert to message pool
   101  			// and assume log driver is hosed
   102  			PutMessage(msg)
   103  			continue
   104  		}
   105  
   106  		if err := r.l.Log(msg); err != nil {
   107  			logrus.WithField("driver", r.l.Name()).
   108  				WithField("container", r.logInfo.ContainerID).
   109  				WithError(err).
   110  				Errorf("Error writing log message")
   111  			logErr = true
   112  		}
   113  	}
   114  	return r.l.Close()
   115  }
   116  
   117  // run consumes messages from the ring buffer and forwards them to the underling
   118  // logger.
   119  // This is run in a goroutine when the RingLogger is created
   120  func (r *RingLogger) run() {
   121  	for {
   122  		if r.closed() {
   123  			return
   124  		}
   125  		msg, err := r.buffer.Dequeue()
   126  		if err != nil {
   127  			// buffer is closed
   128  			return
   129  		}
   130  		if err := r.l.Log(msg); err != nil {
   131  			logrus.WithField("driver", r.l.Name()).
   132  				WithField("container", r.logInfo.ContainerID).
   133  				WithError(err).
   134  				Errorf("Error writing log message")
   135  		}
   136  	}
   137  }
   138  
   139  type messageRing struct {
   140  	mu sync.Mutex
   141  	// signals callers of `Dequeue` to wake up either on `Close` or when a new `Message` is added
   142  	wait *sync.Cond
   143  
   144  	sizeBytes int64 // current buffer size
   145  	maxBytes  int64 // max buffer size size
   146  	queue     []*Message
   147  	closed    bool
   148  }
   149  
   150  func newRing(maxBytes int64) *messageRing {
   151  	queueSize := 1000
   152  	if maxBytes == 0 || maxBytes == 1 {
   153  		// With 0 or 1 max byte size, the maximum size of the queue would only ever be 1
   154  		// message long.
   155  		queueSize = 1
   156  	}
   157  
   158  	r := &messageRing{queue: make([]*Message, 0, queueSize), maxBytes: maxBytes}
   159  	r.wait = sync.NewCond(&r.mu)
   160  	return r
   161  }
   162  
   163  // Enqueue adds a message to the buffer queue
   164  // If the message is too big for the buffer it drops the new message.
   165  // If there are no messages in the queue and the message is still too big, it adds the message anyway.
   166  func (r *messageRing) Enqueue(m *Message) error {
   167  	mSize := int64(len(m.Line))
   168  
   169  	r.mu.Lock()
   170  	if r.closed {
   171  		r.mu.Unlock()
   172  		return errClosed
   173  	}
   174  	if mSize+r.sizeBytes > r.maxBytes && len(r.queue) > 0 {
   175  		r.wait.Signal()
   176  		r.mu.Unlock()
   177  		return nil
   178  	}
   179  
   180  	r.queue = append(r.queue, m)
   181  	r.sizeBytes += mSize
   182  	r.wait.Signal()
   183  	r.mu.Unlock()
   184  	return nil
   185  }
   186  
   187  // Dequeue pulls a message off the queue
   188  // If there are no messages, it waits for one.
   189  // If the buffer is closed, it will return immediately.
   190  func (r *messageRing) Dequeue() (*Message, error) {
   191  	r.mu.Lock()
   192  	for len(r.queue) == 0 && !r.closed {
   193  		r.wait.Wait()
   194  	}
   195  
   196  	if r.closed {
   197  		r.mu.Unlock()
   198  		return nil, errClosed
   199  	}
   200  
   201  	msg := r.queue[0]
   202  	r.queue = r.queue[1:]
   203  	r.sizeBytes -= int64(len(msg.Line))
   204  	r.mu.Unlock()
   205  	return msg, nil
   206  }
   207  
   208  var errClosed = errors.New("closed")
   209  
   210  // Close closes the buffer ensuring no new messages can be added.
   211  // Any callers waiting to dequeue a message will be woken up.
   212  func (r *messageRing) Close() {
   213  	r.mu.Lock()
   214  	if r.closed {
   215  		r.mu.Unlock()
   216  		return
   217  	}
   218  
   219  	r.closed = true
   220  	r.wait.Broadcast()
   221  	r.mu.Unlock()
   222  }
   223  
   224  // Drain drains all messages from the queue.
   225  // This can be used after `Close()` to get any remaining messages that were in queue.
   226  func (r *messageRing) Drain() []*Message {
   227  	r.mu.Lock()
   228  	ls := make([]*Message, 0, len(r.queue))
   229  	ls = append(ls, r.queue...)
   230  	r.sizeBytes = 0
   231  	r.queue = r.queue[:0]
   232  	r.mu.Unlock()
   233  	return ls
   234  }