github.com/jiasir/deis@v1.12.2/logger/storage/ringbuffer/adapter.go (about)

     1  package ringbuffer
     2  
     3  import (
     4  	"container/ring"
     5  	"fmt"
     6  	"sync"
     7  )
     8  
     9  type ringBuffer struct {
    10  	ring  *ring.Ring
    11  	mutex sync.RWMutex
    12  }
    13  
    14  func newRingBuffer(size int) *ringBuffer {
    15  	return &ringBuffer{ring: ring.New(size)}
    16  }
    17  
    18  func (rb *ringBuffer) write(message string) {
    19  	// Get a write lock since writing adjusts the value of the internal ring pointer
    20  	rb.mutex.Lock()
    21  	defer rb.mutex.Unlock()
    22  	rb.ring = rb.ring.Next()
    23  	rb.ring.Value = message
    24  }
    25  
    26  func (rb *ringBuffer) read(lines int) []string {
    27  	if lines <= 0 {
    28  		return []string{}
    29  	}
    30  	// Only need a read lock because nothing we're about to do affects the internal state of the
    31  	// ringBuffer.  Mutliple reads can happen in parallel.  Only writing requires an exclusive lock.
    32  	rb.mutex.RLock()
    33  	defer rb.mutex.RUnlock()
    34  	var start *ring.Ring
    35  	if lines < rb.ring.Len() {
    36  		start = rb.ring.Move(-1 * (lines - 1))
    37  	} else {
    38  		start = rb.ring.Next()
    39  	}
    40  	data := make([]string, 0, lines)
    41  	start.Do(func(line interface{}) {
    42  		if line == nil || lines <= 0 {
    43  			return
    44  		}
    45  		lines--
    46  		data = append(data, line.(string))
    47  	})
    48  	return data
    49  }
    50  
    51  type adapter struct {
    52  	bufferSize  int
    53  	ringBuffers map[string]*ringBuffer
    54  	mutex       sync.Mutex
    55  }
    56  
    57  // NewStorageAdapter returns a pointer to a new instance of an in-memory storage.Adapter.
    58  func NewStorageAdapter(bufferSize int) (*adapter, error) {
    59  	if bufferSize <= 0 {
    60  		return nil, fmt.Errorf("Invalid ringBuffer size: %d", bufferSize)
    61  	}
    62  	return &adapter{bufferSize: bufferSize, ringBuffers: make(map[string]*ringBuffer)}, nil
    63  }
    64  
    65  // Write adds a log message to to an app-specific ringBuffer
    66  func (a *adapter) Write(app string, message string) error {
    67  	// Check first if we might actually have to add to the map of ringBuffer pointers so we can avoid
    68  	// waiting for / obtaining a lock unnecessarily
    69  	rb, ok := a.ringBuffers[app]
    70  	if !ok {
    71  		// Ensure only one goroutine at a time can be adding a ringBuffer to the map of ringBuffers
    72  		// pointers
    73  		a.mutex.Lock()
    74  		defer a.mutex.Unlock()
    75  		rb, ok = a.ringBuffers[app]
    76  		if !ok {
    77  			rb = newRingBuffer(a.bufferSize)
    78  			a.ringBuffers[app] = rb
    79  		}
    80  	}
    81  	rb.write(message)
    82  	return nil
    83  }
    84  
    85  // Read retrieves a specified number of log lines from an app-specific ringBuffer
    86  func (a *adapter) Read(app string, lines int) ([]string, error) {
    87  	rb, ok := a.ringBuffers[app]
    88  	if ok {
    89  		return rb.read(lines), nil
    90  	}
    91  	return nil, fmt.Errorf("Could not find logs for '%s'", app)
    92  }
    93  
    94  // Destroy deletes stored logs for the specified application
    95  func (a *adapter) Destroy(app string) error {
    96  	// Check first if the map of ringBuffer pointers even contains the ringBuffer we intend to
    97  	// delete so we can avoid waiting for / obtaining a lock unnecessarily
    98  	_, ok := a.ringBuffers[app]
    99  	if ok {
   100  		a.mutex.Lock()
   101  		defer a.mutex.Unlock()
   102  		delete(a.ringBuffers, app)
   103  	}
   104  	return nil
   105  }
   106  
   107  func (a *adapter) Reopen() error {
   108  	// No-op
   109  	return nil
   110  }