internal/files/logtail.go (53 lines of code) (raw):
package files
import (
"io"
"os"
"github.com/pkg/errors"
)
// TailFile returns the last max bytes (or the entire file if the file size is
// smaller than max) from the file at path. If the file does not exist, it
// returns a nil slice and no error.
func TailFile(path string, max int64) ([]byte, error) {
f, err := os.Open(path)
if err != nil && os.IsNotExist(err) {
return nil, nil
} else if err != nil {
return nil, errors.Wrap(err, "error opening file")
}
defer f.Close()
fi, err := f.Stat()
if err != nil {
return nil, errors.Wrap(err, "error retrieving file info")
}
size := fi.Size()
n := max
if size < n {
n = size
}
_, err = f.Seek(-n, io.SeekEnd)
if err != nil {
return nil, errors.Wrapf(err, "error seeking file: offset=%d whence=%v", n, io.SeekEnd)
}
b, err := io.ReadAll(io.LimitReader(f, max))
return b, errors.Wrap(err, "error reading from file")
}
func GetFileFromPosition(path string, position int64) ([]byte, error) {
f, err := os.Open(path)
if err != nil && os.IsNotExist(err) {
return nil, nil
} else if err != nil {
return nil, errors.Wrap(err, "error opening file")
}
defer f.Close()
fi, err := f.Stat()
if err != nil {
return nil, errors.Wrap(err, "error retrieving file info")
}
size := fi.Size()
if position >= size {
// No content after position
return make([]byte, 0), nil
}
_, err = f.Seek(position, io.SeekStart)
if err != nil {
return nil, errors.Wrapf(err, "error seeking file: %s, offset=%d", path, position)
}
b, err := io.ReadAll(io.LimitReader(f, size-position))
return b, errors.Wrap(err, "error reading from file: "+path)
}