diff --git a/cmd/gotail/gotail.go b/cmd/gotail/gotail.go index 99e6e97..12b2c62 100644 --- a/cmd/gotail/gotail.go +++ b/cmd/gotail/gotail.go @@ -9,9 +9,10 @@ import ( "os" ) -func args2config() tail.Config { +func args2config() (tail.Config, int64) { config := tail.Config{Follow: true} - flag.Int64Var(&config.Location, "n", 0, "tail from the last Nth location") + n := int64(0) + flag.Int64Var(&n, "n", 0, "tail from the last Nth location") flag.BoolVar(&config.Follow, "f", false, "wait for additional data to be appended to the file") flag.BoolVar(&config.ReOpen, "F", false, "follow, and track file rename/rotation") flag.BoolVar(&config.Poll, "p", false, "use polling, instead of inotify") @@ -19,16 +20,20 @@ func args2config() tail.Config { if config.ReOpen { config.Follow = true } - return config + return config, n } func main() { - config := args2config() + config, n := args2config() if flag.NFlag() < 1 { fmt.Println("need one or more files as arguments") os.Exit(1) } + if n != 0 { + config.Location = &tail.SeekInfo{-n, os.SEEK_END} + } + done := make(chan bool) for _, filename := range flag.Args() { go tailFile(filename, config, done) diff --git a/tail.go b/tail.go index d38e540..91c2600 100644 --- a/tail.go +++ b/tail.go @@ -24,17 +24,20 @@ type Line struct { // log line itself. } +type SeekInfo struct { + Offset int64 + Whence int // os.SEEK_* +} + // Config is used to specify how a file must be tailed. type Config struct { - Location int64 // For positive location, tail after first N - // lines; for negative, tail from last N lines; - // for zero, tail from end. If -1, do not seek. - Follow bool // Continue looking for new lines (tail -f) - ReOpen bool // Reopen recreated files (tail -F) - MustExist bool // Fail early if the file does not exist - Poll bool // Poll for file changes instead of using inotify - MaxLineSize int // If non-zero, split longer lines into multiple lines - LimitRate int64 // If non-zero, limit the rate of read log lines + Location *SeekInfo // Seek before tailing + Follow bool // Continue looking for new lines (tail -f) + ReOpen bool // Reopen recreated files (tail -F) + MustExist bool // Fail early if the file does not exist + Poll bool // Poll for file changes instead of using inotify + MaxLineSize int // If non-zero, split longer lines into multiple lines + LimitRate int64 // If non-zero, limit the rate of read log lines // by this much per second. } @@ -141,16 +144,9 @@ func (tail *Tail) tailFileSync() { } // Seek to requested location on first open of the file. - if tail.Location != -1 { - var err error - switch { - case tail.Location <= 0: - // Seek relative to file end - _, err = tail.file.Seek(tail.Location, os.SEEK_END) - default: - // Seek from file beginning - _, err = tail.file.Seek(tail.Location, os.SEEK_SET) - } + if tail.Location != nil { + _, err := tail.file.Seek(tail.Location.Offset, tail.Location.Whence) + // log.Printf("Seeked %s - %+v\n", tail.Filename, tail.Location) if err != nil { tail.Killf("Seek error on %s: %s", tail.Filename, err) return