k8s.io/kubernetes@v1.29.3/pkg/util/tail/tail.go (about) 1 /* 2 Copyright 2017 The Kubernetes Authors. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package tail 18 19 import ( 20 "bytes" 21 "io" 22 "os" 23 ) 24 25 const ( 26 // blockSize is the block size used in tail. 27 blockSize = 1024 28 ) 29 30 var ( 31 // eol is the end-of-line sign in the log. 32 eol = []byte{'\n'} 33 ) 34 35 // ReadAtMost reads at most max bytes from the end of the file identified by path or 36 // returns an error. It returns true if the file was longer than max. It will 37 // allocate up to max bytes. 38 func ReadAtMost(path string, max int64) ([]byte, bool, error) { 39 f, err := os.Open(path) 40 if err != nil { 41 return nil, false, err 42 } 43 defer f.Close() 44 fi, err := f.Stat() 45 if err != nil { 46 return nil, false, err 47 } 48 size := fi.Size() 49 if size == 0 { 50 return nil, false, nil 51 } 52 if size < max { 53 max = size 54 } 55 offset, err := f.Seek(-max, io.SeekEnd) 56 if err != nil { 57 return nil, false, err 58 } 59 data, err := io.ReadAll(f) 60 return data, offset > 0, err 61 } 62 63 // FindTailLineStartIndex returns the start of last nth line. 64 // * If n < 0, return the beginning of the file. 65 // * If n >= 0, return the beginning of last nth line. 66 // Notice that if the last line is incomplete (no end-of-line), it will not be counted 67 // as one line. 68 func FindTailLineStartIndex(f io.ReadSeeker, n int64) (int64, error) { 69 if n < 0 { 70 return 0, nil 71 } 72 size, err := f.Seek(0, io.SeekEnd) 73 if err != nil { 74 return 0, err 75 } 76 var left, cnt int64 77 buf := make([]byte, blockSize) 78 for right := size; right > 0 && cnt <= n; right -= blockSize { 79 left = right - blockSize 80 if left < 0 { 81 left = 0 82 buf = make([]byte, right) 83 } 84 if _, err := f.Seek(left, io.SeekStart); err != nil { 85 return 0, err 86 } 87 if _, err := f.Read(buf); err != nil { 88 return 0, err 89 } 90 cnt += int64(bytes.Count(buf, eol)) 91 } 92 for ; cnt > n; cnt-- { 93 idx := bytes.Index(buf, eol) + 1 94 buf = buf[idx:] 95 left += int64(idx) 96 } 97 return left, nil 98 }