github.com/netdata/go.d.plugin@v0.58.1/pkg/logs/lastline.go (about)

     1  // SPDX-License-Identifier: GPL-3.0-or-later
     2  
     3  package logs
     4  
     5  import (
     6  	"errors"
     7  	"os"
     8  
     9  	"github.com/clbanning/rfile/v2"
    10  )
    11  
    12  const DefaultMaxLineWidth = 4 * 1024 // assume disk block size is 4K
    13  
    14  var ErrTooLongLine = errors.New("too long line")
    15  
    16  // ReadLastLine returns the last line of the file and any read error encountered.
    17  // It expects last line width <= maxLineWidth.
    18  // If maxLineWidth <= 0, it defaults to DefaultMaxLineWidth.
    19  func ReadLastLine(filename string, maxLineWidth int64) ([]byte, error) {
    20  	if maxLineWidth <= 0 {
    21  		maxLineWidth = DefaultMaxLineWidth
    22  	}
    23  	f, err := os.Open(filename)
    24  	if err != nil {
    25  		return nil, err
    26  	}
    27  	defer func() { _ = f.Close() }()
    28  
    29  	stat, _ := f.Stat()
    30  	endPos := stat.Size()
    31  	if endPos == 0 {
    32  		return []byte{}, nil
    33  	}
    34  	startPos := endPos - maxLineWidth
    35  	if startPos < 0 {
    36  		startPos = 0
    37  	}
    38  	buf := make([]byte, endPos-startPos)
    39  	n, err := f.ReadAt(buf, startPos)
    40  	if err != nil {
    41  		return nil, err
    42  	}
    43  	lnPos := 0
    44  	foundLn := false
    45  	for i := n - 2; i >= 0; i-- {
    46  		ch := buf[i]
    47  		if ch == '\n' {
    48  			foundLn = true
    49  			lnPos = i
    50  			break
    51  		}
    52  	}
    53  	if foundLn {
    54  		return buf[lnPos+1 : n], nil
    55  	}
    56  	if startPos == 0 {
    57  		return buf[0:n], nil
    58  	}
    59  
    60  	return nil, ErrTooLongLine
    61  }
    62  
    63  func ReadLastLines(filename string, n uint) ([]string, error) {
    64  	return rfile.Tail(filename, int(n))
    65  }