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  }