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 }