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