github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/state/logdb/buf.go (about) 1 // Copyright 2017 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package logdb 5 6 import ( 7 "sync" 8 "time" 9 10 "github.com/juju/clock" 11 "github.com/juju/errors" 12 13 "github.com/juju/juju/state" 14 ) 15 16 // Logger provides an interface for writing log records. 17 type Logger interface { 18 // Log writes the given log records to the logger's storage. 19 Log([]state.LogRecord) error 20 } 21 22 // BufferedLogger wraps a Logger, providing a buffer that 23 // accumulates log messages, flushing them to the underlying logger 24 // when enough messages have been accumulated. 25 type BufferedLogger struct { 26 l Logger 27 clock clock.Clock 28 flushInterval time.Duration 29 30 mu sync.Mutex 31 buf []state.LogRecord 32 flushTimer clock.Timer 33 } 34 35 // NewBufferedLogger returns a new BufferedLogger, wrapping the given 36 // Logger with a buffer of the specified size and flush interval. 37 func NewBufferedLogger( 38 l Logger, 39 bufferSize int, 40 flushInterval time.Duration, 41 clock clock.Clock, 42 ) *BufferedLogger { 43 return &BufferedLogger{ 44 l: l, 45 buf: make([]state.LogRecord, 0, bufferSize), 46 clock: clock, 47 flushInterval: flushInterval, 48 } 49 } 50 51 // Log is part of the Logger interface. 52 // 53 // BufferedLogger's Log implementation will buffer log records up to 54 // the specified capacity and duration; after either of which is exceeded, 55 // the records will be flushed to the underlying logger. 56 func (b *BufferedLogger) Log(in []state.LogRecord) error { 57 b.mu.Lock() 58 defer b.mu.Unlock() 59 for len(in) > 0 { 60 r := cap(b.buf) - len(b.buf) 61 n := len(in) 62 if n > r { 63 n = r 64 } 65 b.buf = append(b.buf, in[:n]...) 66 in = in[n:] 67 if len(b.buf) >= cap(b.buf) { 68 if err := b.flush(); err != nil { 69 return errors.Trace(err) 70 } 71 } 72 } 73 if len(b.buf) > 0 && b.flushTimer == nil { 74 b.flushTimer = b.clock.AfterFunc(b.flushInterval, b.flushOnTimer) 75 } 76 return nil 77 } 78 79 // Flush flushes any buffered log records to the underlying Logger. 80 func (b *BufferedLogger) Flush() error { 81 b.mu.Lock() 82 b.mu.Unlock() 83 return b.flush() 84 } 85 86 func (b *BufferedLogger) flushOnTimer() { 87 b.mu.Lock() 88 defer b.mu.Unlock() 89 // Can't do anything about errors here, except to 90 // ignore them and let the Log() method report them 91 // when the buffer fills up. 92 b.flush() 93 } 94 95 // flush flushes any buffered log records to the underlying Logger, and stops 96 // the flush timer if there is one. The caller must be holding b.mu. 97 func (b *BufferedLogger) flush() error { 98 if b.flushTimer != nil { 99 b.flushTimer.Stop() 100 b.flushTimer = nil 101 } 102 if len(b.buf) > 0 { 103 if err := b.l.Log(b.buf); err != nil { 104 return errors.Trace(err) 105 } 106 b.buf = b.buf[:0] 107 } 108 return nil 109 }