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  }