github.com/demonoid81/moby@v0.0.0-20200517203328-62dd8e17c460/daemon/logger/ring.go (about) 1 package logger // import "github.com/demonoid81/moby/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 }