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 }