You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

455 lines
11 KiB

  1. // Copyright (c) 2019 FOSS contributors of https://github.com/nxadm/tail
  2. // Copyright (c) 2015 HPE Software Inc. All rights reserved.
  3. // Copyright (c) 2013 ActiveState Software Inc. All rights reserved.
  4. //nxadm/tail provides a Go library that emulates the features of the BSD `tail`
  5. //program. The library comes with full support for truncation/move detection as
  6. //it is designed to work with log rotation tools. The library works on all
  7. //operating systems supported by Go, including POSIX systems like Linux and
  8. //*BSD, and MS Windows. Go 1.9 is the oldest compiler release supported.
  9. package tail
  10. import (
  11. "bufio"
  12. "errors"
  13. "fmt"
  14. "io"
  15. "io/ioutil"
  16. "log"
  17. "os"
  18. "strings"
  19. "sync"
  20. "time"
  21. "github.com/nxadm/tail/ratelimiter"
  22. "github.com/nxadm/tail/util"
  23. "github.com/nxadm/tail/watch"
  24. "gopkg.in/tomb.v1"
  25. )
  26. var (
  27. // ErrStop is returned when the tail of a file has been marked to be stopped.
  28. ErrStop = errors.New("tail should now stop")
  29. )
  30. type Line struct {
  31. Text string // The contents of the file
  32. Num int // The line number
  33. SeekInfo SeekInfo // SeekInfo
  34. Time time.Time // Present time
  35. Err error // Error from tail
  36. }
  37. // Deprecated: this function is no longer used internally and it has little of no
  38. // use in the API. As such, it will be removed from the API in a future major
  39. // release.
  40. //
  41. // NewLine returns a * pointer to a Line struct.
  42. func NewLine(text string, lineNum int) *Line {
  43. return &Line{text, lineNum, SeekInfo{}, time.Now(), nil}
  44. }
  45. // SeekInfo represents arguments to io.Seek. See: https://golang.org/pkg/io/#SectionReader.Seek
  46. type SeekInfo struct {
  47. Offset int64
  48. Whence int
  49. }
  50. type logger interface {
  51. Fatal(v ...interface{})
  52. Fatalf(format string, v ...interface{})
  53. Fatalln(v ...interface{})
  54. Panic(v ...interface{})
  55. Panicf(format string, v ...interface{})
  56. Panicln(v ...interface{})
  57. Print(v ...interface{})
  58. Printf(format string, v ...interface{})
  59. Println(v ...interface{})
  60. }
  61. // Config is used to specify how a file must be tailed.
  62. type Config struct {
  63. // File-specifc
  64. Location *SeekInfo // Tail from this location. If nil, start at the beginning of the file
  65. ReOpen bool // Reopen recreated files (tail -F)
  66. MustExist bool // Fail early if the file does not exist
  67. Poll bool // Poll for file changes instead of using the default inotify
  68. Pipe bool // The file is a named pipe (mkfifo)
  69. // Generic IO
  70. Follow bool // Continue looking for new lines (tail -f)
  71. MaxLineSize int // If non-zero, split longer lines into multiple lines
  72. // Optionally, use a ratelimiter (e.g. created by the ratelimiter/NewLeakyBucket function)
  73. RateLimiter *ratelimiter.LeakyBucket
  74. // Optionally use a Logger. When nil, the Logger is set to tail.DefaultLogger.
  75. // To disable logging, set it to tail.DiscardingLogger
  76. Logger logger
  77. }
  78. type Tail struct {
  79. Filename string // The filename
  80. Lines chan *Line // A consumable channel of *Line
  81. Config // Tail.Configuration
  82. file *os.File
  83. reader *bufio.Reader
  84. lineNum int
  85. watcher watch.FileWatcher
  86. changes *watch.FileChanges
  87. tomb.Tomb // provides: Done, Kill, Dying
  88. lk sync.Mutex
  89. }
  90. var (
  91. // DefaultLogger logs to os.Stderr and it is used when Config.Logger == nil
  92. DefaultLogger = log.New(os.Stderr, "", log.LstdFlags)
  93. // DiscardingLogger can be used to disable logging output
  94. DiscardingLogger = log.New(ioutil.Discard, "", 0)
  95. )
  96. // TailFile begins tailing the file. And returns a pointer to a Tail struct
  97. // and an error. An output stream is made available via the Tail.Lines
  98. // channel (e.g. to be looped and printed). To handle errors during tailing,
  99. // after finishing reading from the Lines channel, invoke the `Wait` or `Err`
  100. // method on the returned *Tail.
  101. func TailFile(filename string, config Config) (*Tail, error) {
  102. if config.ReOpen && !config.Follow {
  103. util.Fatal("cannot set ReOpen without Follow.")
  104. }
  105. t := &Tail{
  106. Filename: filename,
  107. Lines: make(chan *Line),
  108. Config: config,
  109. }
  110. // when Logger was not specified in config, use default logger
  111. if t.Logger == nil {
  112. t.Logger = DefaultLogger
  113. }
  114. if t.Poll {
  115. t.watcher = watch.NewPollingFileWatcher(filename)
  116. } else {
  117. t.watcher = watch.NewInotifyFileWatcher(filename)
  118. }
  119. if t.MustExist {
  120. var err error
  121. t.file, err = OpenFile(t.Filename)
  122. if err != nil {
  123. return nil, err
  124. }
  125. }
  126. go t.tailFileSync()
  127. return t, nil
  128. }
  129. // Tell returns the file's current position, like stdio's ftell() and an error.
  130. // Beware that this value may not be completely accurate because one line from
  131. // the chan(tail.Lines) may have been read already.
  132. func (tail *Tail) Tell() (offset int64, err error) {
  133. if tail.file == nil {
  134. return
  135. }
  136. offset, err = tail.file.Seek(0, io.SeekCurrent)
  137. if err != nil {
  138. return
  139. }
  140. tail.lk.Lock()
  141. defer tail.lk.Unlock()
  142. if tail.reader == nil {
  143. return
  144. }
  145. offset -= int64(tail.reader.Buffered())
  146. return
  147. }
  148. // Stop stops the tailing activity.
  149. func (tail *Tail) Stop() error {
  150. tail.Kill(nil)
  151. return tail.Wait()
  152. }
  153. // StopAtEOF stops tailing as soon as the end of the file is reached. The function
  154. // returns an error,
  155. func (tail *Tail) StopAtEOF() error {
  156. tail.Kill(errStopAtEOF)
  157. return tail.Wait()
  158. }
  159. var errStopAtEOF = errors.New("tail: stop at eof")
  160. func (tail *Tail) close() {
  161. close(tail.Lines)
  162. tail.closeFile()
  163. }
  164. func (tail *Tail) closeFile() {
  165. if tail.file != nil {
  166. tail.file.Close()
  167. tail.file = nil
  168. }
  169. }
  170. func (tail *Tail) reopen() error {
  171. tail.closeFile()
  172. tail.lineNum = 0
  173. for {
  174. var err error
  175. tail.file, err = OpenFile(tail.Filename)
  176. if err != nil {
  177. if os.IsNotExist(err) {
  178. tail.Logger.Printf("Waiting for %s to appear...", tail.Filename)
  179. if err := tail.watcher.BlockUntilExists(&tail.Tomb); err != nil {
  180. if err == tomb.ErrDying {
  181. return err
  182. }
  183. return fmt.Errorf("Failed to detect creation of %s: %s", tail.Filename, err)
  184. }
  185. continue
  186. }
  187. return fmt.Errorf("Unable to open file %s: %s", tail.Filename, err)
  188. }
  189. break
  190. }
  191. return nil
  192. }
  193. func (tail *Tail) readLine() (string, error) {
  194. tail.lk.Lock()
  195. line, err := tail.reader.ReadString('\n')
  196. tail.lk.Unlock()
  197. if err != nil {
  198. // Note ReadString "returns the data read before the error" in
  199. // case of an error, including EOF, so we return it as is. The
  200. // caller is expected to process it if err is EOF.
  201. return line, err
  202. }
  203. line = strings.TrimRight(line, "\n")
  204. return line, err
  205. }
  206. func (tail *Tail) tailFileSync() {
  207. defer tail.Done()
  208. defer tail.close()
  209. if !tail.MustExist {
  210. // deferred first open.
  211. err := tail.reopen()
  212. if err != nil {
  213. if err != tomb.ErrDying {
  214. tail.Kill(err)
  215. }
  216. return
  217. }
  218. }
  219. // Seek to requested location on first open of the file.
  220. if tail.Location != nil {
  221. _, err := tail.file.Seek(tail.Location.Offset, tail.Location.Whence)
  222. if err != nil {
  223. tail.Killf("Seek error on %s: %s", tail.Filename, err)
  224. return
  225. }
  226. }
  227. tail.openReader()
  228. // Read line by line.
  229. for {
  230. // do not seek in named pipes
  231. if !tail.Pipe {
  232. // grab the position in case we need to back up in the event of a half-line
  233. if _, err := tail.Tell(); err != nil {
  234. tail.Kill(err)
  235. return
  236. }
  237. }
  238. line, err := tail.readLine()
  239. // Process `line` even if err is EOF.
  240. if err == nil {
  241. cooloff := !tail.sendLine(line)
  242. if cooloff {
  243. // Wait a second before seeking till the end of
  244. // file when rate limit is reached.
  245. msg := ("Too much log activity; waiting a second before resuming tailing")
  246. offset, _ := tail.Tell()
  247. tail.Lines <- &Line{msg, tail.lineNum, SeekInfo{Offset: offset}, time.Now(), errors.New(msg)}
  248. select {
  249. case <-time.After(time.Second):
  250. case <-tail.Dying():
  251. return
  252. }
  253. if err := tail.seekEnd(); err != nil {
  254. tail.Kill(err)
  255. return
  256. }
  257. }
  258. } else if err == io.EOF {
  259. if !tail.Follow {
  260. if line != "" {
  261. tail.sendLine(line)
  262. }
  263. return
  264. }
  265. if tail.Follow && line != "" {
  266. tail.sendLine(line)
  267. if err := tail.seekEnd(); err != nil {
  268. tail.Kill(err)
  269. return
  270. }
  271. }
  272. // When EOF is reached, wait for more data to become
  273. // available. Wait strategy is based on the `tail.watcher`
  274. // implementation (inotify or polling).
  275. err := tail.waitForChanges()
  276. if err != nil {
  277. if err != ErrStop {
  278. tail.Kill(err)
  279. }
  280. return
  281. }
  282. } else {
  283. // non-EOF error
  284. tail.Killf("Error reading %s: %s", tail.Filename, err)
  285. return
  286. }
  287. select {
  288. case <-tail.Dying():
  289. if tail.Err() == errStopAtEOF {
  290. continue
  291. }
  292. return
  293. default:
  294. }
  295. }
  296. }
  297. // waitForChanges waits until the file has been appended, deleted,
  298. // moved or truncated. When moved or deleted - the file will be
  299. // reopened if ReOpen is true. Truncated files are always reopened.
  300. func (tail *Tail) waitForChanges() error {
  301. if tail.changes == nil {
  302. pos, err := tail.file.Seek(0, io.SeekCurrent)
  303. if err != nil {
  304. return err
  305. }
  306. tail.changes, err = tail.watcher.ChangeEvents(&tail.Tomb, pos)
  307. if err != nil {
  308. return err
  309. }
  310. }
  311. select {
  312. case <-tail.changes.Modified:
  313. return nil
  314. case <-tail.changes.Deleted:
  315. tail.changes = nil
  316. if tail.ReOpen {
  317. // XXX: we must not log from a library.
  318. tail.Logger.Printf("Re-opening moved/deleted file %s ...", tail.Filename)
  319. if err := tail.reopen(); err != nil {
  320. return err
  321. }
  322. tail.Logger.Printf("Successfully reopened %s", tail.Filename)
  323. tail.openReader()
  324. return nil
  325. }
  326. tail.Logger.Printf("Stopping tail as file no longer exists: %s", tail.Filename)
  327. return ErrStop
  328. case <-tail.changes.Truncated:
  329. // Always reopen truncated files (Follow is true)
  330. tail.Logger.Printf("Re-opening truncated file %s ...", tail.Filename)
  331. if err := tail.reopen(); err != nil {
  332. return err
  333. }
  334. tail.Logger.Printf("Successfully reopened truncated %s", tail.Filename)
  335. tail.openReader()
  336. return nil
  337. case <-tail.Dying():
  338. return ErrStop
  339. }
  340. }
  341. func (tail *Tail) openReader() {
  342. tail.lk.Lock()
  343. if tail.MaxLineSize > 0 {
  344. // add 2 to account for newline characters
  345. tail.reader = bufio.NewReaderSize(tail.file, tail.MaxLineSize+2)
  346. } else {
  347. tail.reader = bufio.NewReader(tail.file)
  348. }
  349. tail.lk.Unlock()
  350. }
  351. func (tail *Tail) seekEnd() error {
  352. return tail.seekTo(SeekInfo{Offset: 0, Whence: io.SeekEnd})
  353. }
  354. func (tail *Tail) seekTo(pos SeekInfo) error {
  355. _, err := tail.file.Seek(pos.Offset, pos.Whence)
  356. if err != nil {
  357. return fmt.Errorf("Seek error on %s: %s", tail.Filename, err)
  358. }
  359. // Reset the read buffer whenever the file is re-seek'ed
  360. tail.reader.Reset(tail.file)
  361. return nil
  362. }
  363. // sendLine sends the line(s) to Lines channel, splitting longer lines
  364. // if necessary. Return false if rate limit is reached.
  365. func (tail *Tail) sendLine(line string) bool {
  366. now := time.Now()
  367. lines := []string{line}
  368. // Split longer lines
  369. if tail.MaxLineSize > 0 && len(line) > tail.MaxLineSize {
  370. lines = util.PartitionString(line, tail.MaxLineSize)
  371. }
  372. for _, line := range lines {
  373. tail.lineNum++
  374. offset, _ := tail.Tell()
  375. select {
  376. case tail.Lines <- &Line{line, tail.lineNum, SeekInfo{Offset: offset}, now, nil}:
  377. case <-tail.Dying():
  378. return true
  379. }
  380. }
  381. if tail.Config.RateLimiter != nil {
  382. ok := tail.Config.RateLimiter.Pour(uint16(len(lines)))
  383. if !ok {
  384. tail.Logger.Printf("Leaky bucket full (%v); entering 1s cooloff period.",
  385. tail.Filename)
  386. return false
  387. }
  388. }
  389. return true
  390. }
  391. // Cleanup removes inotify watches added by the tail package. This function is
  392. // meant to be invoked from a process's exit handler. Linux kernel may not
  393. // automatically remove inotify watches after the process exits.
  394. // If you plan to re-read a file, don't call Cleanup in between.
  395. func (tail *Tail) Cleanup() {
  396. watch.Cleanup(tail.Filename)
  397. }