Andrey Smirnov 0a4645fe80 feat: implement circular buffer for system logs
This replaces logging to files with inotify following to pure in-memory
circular buffer which grows on demand capped at specified maximum
capacity.

The concern with previous approach was that logs on tmpfs were growing
without any bound potentially consuming all the node memory.

Signed-off-by: Andrey Smirnov <smirnov.andrey@gmail.com>
2020-06-26 15:33:54 -07:00

104 lines
1.8 KiB
Go

// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package circular
import (
"io"
"sync/atomic"
)
// Reader implements seekable reader with local position in the Buffer which
// reads from the fixed part of the buffer.
//
// Reader is not safe to be used with concurrent Read/Seek operations.
type Reader struct {
buf *Buffer
startOff, endOff int64
off int64
closed uint32
}
// Read implements io.Reader.
func (r *Reader) Read(p []byte) (n int, err error) {
if atomic.LoadUint32(&r.closed) > 0 {
err = ErrClosed
return
}
r.buf.mu.Lock()
defer r.buf.mu.Unlock()
if r.off < r.buf.off-int64(r.buf.opt.MaxCapacity) {
// reader is falling too much behind
err = ErrOutOfSync
return
}
if r.off == r.endOff {
err = io.EOF
return
}
if len(p) == 0 {
return
}
n = int(r.endOff - r.off)
if n > len(p) {
n = len(p)
}
i := int(r.off % int64(r.buf.opt.MaxCapacity))
l := r.buf.opt.MaxCapacity - i
if l < n {
copy(p, r.buf.data[i:])
copy(p[l:], r.buf.data[:n-l])
} else {
copy(p, r.buf.data[i:i+n])
}
r.off += int64(n)
return n, err
}
// Close implements io.Closer.
func (r *Reader) Close() error {
atomic.StoreUint32(&r.closed, 1)
return nil
}
// Seek implements io.Seeker.
func (r *Reader) Seek(offset int64, whence int) (int64, error) {
newOff := r.off
switch whence {
case io.SeekCurrent:
newOff += offset
case io.SeekEnd:
newOff = r.endOff + offset
case io.SeekStart:
newOff = r.startOff + offset
}
if newOff < r.startOff {
return r.off - r.startOff, ErrSeekBeforeStart
}
if newOff > r.endOff {
newOff = r.endOff
}
r.off = newOff
return r.off - r.startOff, nil
}