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 }