| 11 | ) |
| 12 | |
| 13 | func tailFile(ctx context.Context, file string, poll bool, dest *os.File) { |
| 14 | defer wg.Done() |
| 15 | |
| 16 | var isPipe bool |
| 17 | var errCount int |
| 18 | |
| 19 | s, err := os.Stat(file) |
| 20 | if err != nil { |
| 21 | log.Printf("Warning: unable to stat %s: %s", file, err) |
| 22 | errCount++ |
| 23 | isPipe = false |
| 24 | } else { |
| 25 | isPipe = s.Mode()&os.ModeNamedPipe != 0 |
| 26 | } |
| 27 | |
| 28 | t, err := tail.TailFile(file, tail.Config{ |
| 29 | Follow: true, |
| 30 | ReOpen: true, |
| 31 | Poll: poll, |
| 32 | Logger: tail.DiscardingLogger, |
| 33 | Pipe: isPipe, |
| 34 | }) |
| 35 | if err != nil { |
| 36 | log.Fatalf("unable to tail %s: %s", file, err) |
| 37 | } |
| 38 | |
| 39 | defer func() { |
| 40 | t.Stop() |
| 41 | t.Cleanup() |
| 42 | }() |
| 43 | |
| 44 | // main loop |
| 45 | for { |
| 46 | select { |
| 47 | // if the channel is done, then exit the loop |
| 48 | case <-ctx.Done(): |
| 49 | return |
| 50 | // get the next log line and echo it out |
| 51 | case line := <-t.Lines: |
| 52 | if line == nil { |
| 53 | // Check if there's an actual error |
| 54 | if err := t.Err(); err != nil { |
| 55 | log.Printf("Warning: unable to tail %s: %s", file, err) |
| 56 | errCount++ |
| 57 | if errCount > 30 { |
| 58 | log.Fatalf("Logged %d consecutive errors while tailing. Exiting", errCount) |
| 59 | } |
| 60 | time.Sleep(2 * time.Second) // Sleep for 2 seconds before retrying |
| 61 | continue |
| 62 | } |
| 63 | return |
| 64 | } else { |
| 65 | fmt.Fprintln(dest, line.Text) |
| 66 | errCount = 0 // Zero the error count |
| 67 | } |
| 68 | } |
| 69 | } |
| 70 | } |