func (t *Tailor) Lines() chan Line {
	return t.lines
}
Lines returns chanel of read lines.
package tailor
import (
	"bufio"
	"context"
	"io"
	"os"
	"sync/atomic"
	"time"
	"github.com/pkg/errors"
)
type Tailor struct {
	fileName string
	file     *os.File
	opts options
	lastPos  int64
	lastSize int64
	lag      int64
	lines chan Line
	errs  chan error
	working int32
}
func New(filename string, opts ...Option) *Tailor {
	t := &Tailor{
		fileName: filename,
	}
	for _, p := range [][]Option{withDefaultOptions(), opts} {
		for _, o := range p {
			o(&t.opts)
		}
	}
	return t
}
func (t *Tailor) Run(ctx context.Context) error {
	if !atomic.CompareAndSwapInt32(&t.working, 0, 1) {
		return errors.New("already working")
	}
	failFinalizer := func() {
		if t.file != nil {
			_ = t.file.Close()
		}
		atomic.StoreInt32(&t.working, 0)
	}
	err := t.openFile(t.opts.runOffset, t.opts.runWhence)
	if err != nil {
		failFinalizer()
		return errors.Wrap(err, "can't open file for tailing")
	}
	t.readLoop(ctx)
	return nil
}
func (t *Tailor) readLoop(ctx context.Context) {
	t.lines = make(chan Line)
	t.errs = make(chan error)
	go func() {
		defer func() {
			if t.file != nil {
				err := t.file.Close()
				if err != nil {
					t.errs <- errors.Wrap(err, "error closing file")
				}
			}
			close(t.lines)
			close(t.errs)
			if t.opts.rateLimiter != nil {
				t.opts.rateLimiter.Close()
			}
			atomic.StoreInt32(&t.working, 0)
		}()
		r := bufio.NewReaderSize(t.file, t.opts.bufioReaderPoolSize)
		lagReporter := time.NewTicker(t.opts.updateLagInterval)
		defer lagReporter.Stop()
		pollerTimeout := t.opts.pollerTimeout
		for {
			select {
			case <-ctx.Done():
				return
			case <-lagReporter.C:
				err := t.updateFileStatus()
				if err != nil {
					t.errs <- errors.Wrap(err, "error getting file status")
					break
				}
			default:
			}
			var (
				part []byte
				line []byte
				err  error
			)
			for i := 0; i < 5; i++ {
				part = part[:0]
				part, err = r.ReadBytes('\n')
				if err == nil || err == io.EOF {
					if len(line) == 0 && len(part) > 0 && part[len(part)-1] == '\n' {
						line = part
						break
					}
				} else {
					t.errs <- errors.Wrap(err, "error reading line")
					return
				}
				line = append(line, part...)
				if len(line) == 0 && err == io.EOF {
					break
				}
				if line[len(line)-1] == '\n' {
					break
				}
				pollerTimeout = t.exponentialSleep(pollerTimeout, time.Second)
			}
			if len(line) == 0 && err == io.EOF {
				isSameFile, err := t.isFileStillTheSame()
				if err != nil {
					t.errs <- errors.Wrap(err, "error checking that file is the same")
					return
				}
				if !isSameFile {
					err := t.file.Close()
					if err != nil {
						t.errs <- errors.Wrap(err, "error closing current file")
					}
					err = t.openFile(t.opts.reopenOffset, t.opts.reopenWhence)
					if err != nil {
						t.errs <- errors.Wrap(err, "error reopening file")
						return
					}
					r = bufio.NewReaderSize(t.file, t.opts.bufioReaderPoolSize)
					pollerTimeout = t.opts.pollerTimeout
					continue
				}
				pollerTimeout = t.exponentialSleep(pollerTimeout, 5*time.Second)
				continue
			}
			pollerTimeout = t.opts.pollerTimeout
			if t.opts.rateLimiter == nil || t.opts.rateLimiter.Allow() {
				line := Line{
					line:     line,
					fileName: t.fileName,
				}
				if t.opts.leakyBucket {
					select {
					case t.lines <- line:
					default:
					}
					continue
				}
				t.lines <- line
			}
		}
	}()
}