nxadm / tail

[Revamped] Go package for reading from continuously updated files (tail -f)
MIT License
340 stars 64 forks source link

Prevent a full file read when a file is reopened after truncation #77

Open ouwe-knutselaar opened 3 weeks ago

ouwe-knutselaar commented 3 weeks ago

L.S.

I have an issue with Tail. We have an application that watches a file to send changes to an event bus to generate alerts. The issue is that when the file is truncated and reopened it re-reads the file. The problem is that in our case also all the lines are send to the event bus again. I was digging in to the docs but I could not find a ways to prevent this.

So is there an option in Tail to go to directly to the end of a file without fully reading it when a file is reopened after a truncate?

KaczDev commented 2 weeks ago

Hey, just had the same issue but I think I managed to resolve it.

Have you tried setting the Location when creating the Tail with config? This is my conf and it doesn't read the file from the start. I only want to listen for the new logs.

t, err := tail.TailFile(catalinaPath,
    tail.Config{
        Follow:        true,
        CompleteLines: true,
        Location:      &tail.SeekInfo{Offset: 0, Whence: io.SeekEnd},
    })
ouwe-knutselaar commented 2 weeks ago

I also found this workaround:

 for line := range tailfile.Lines {
        pos, _ := tailfile.Tell()
        if pos != line.SeekInfo.Offset {
            continue
        }
        sendNewLogLine(line.Text)
    }

It works a little, however the last line in a truncated file is always send this eample.

ouwe-knutselaar commented 2 weeks ago

What also Could be done is to add an extra field in the config struct: ToEndAfterTruncate bool // Go to the end of a file after a truncation to prevent double file read

And then add this piece of code to the function waitForChanges()

    case <-tail.changes.Truncated:
        // Always reopen truncated files (Follow is true)
        tail.Logger.Printf("Re-opening truncated file %s ...", tail.Filename)
        if err := tail.reopen(); err != nil {
            return err
        }
        tail.Logger.Printf("Successfully reopened truncated %s", tail.Filename)
        tail.openReader()

        // New snippet
        if tail.ToEndAfterTruncate {
            tail.Logger.Printf("Jump to the end of the file after being truncated %s", tail.Filename)
            if err := tail.seekEnd(); err != nil {
                return err
            }
        }
        // End

        return nil

Test it locally and it works really nice.