CLI AI

Tail multiple log files at once

2026-06-17

The obvious choice is tail -f app.log db.log, but there's a gotcha hiding in production: when logrotate runs, it renames the file and creates a new one, -f keeps the old file descriptor open and you stare at a frozen screen. The fix is one character: -F reopens the file by name instead of inode. Here's when to use each variant.

1. Show both files with headers separating them

clai
$ clai show me both these logs at the same time→ tail -n +1 app.log db.log==> app.log <==line A1==> db.log <==line B1

When you tail multiple files, tail automatically prints headers like ==> filename <== to show which lines came from which source. That's crucial — without it you won't know which log a line belongs to.

2. Follow both files as new lines arrive

clai
$ clai follow both logs as they write new lines→ tail -f app.log db.log

The headers print again every time the source switches. While one file is being written to, you see no header; when the other file gets a line, the header appears.

3. Survive log rotation and keep reading

clai
$ clai follow the log so it doesn't go silent after rotation→ tail -F /var/log/app/app.log

This is the line that matters. -f holds open the file descriptor: after logrotate renames the file and creates a new one, tail -f stays reading the renamed copy and never sees new lines. -F watches the filename and reopens the file. Always use -F in production.

4. Tag each line with the source filename

clai
$ clai show both logs mixed together but label each line with the filename→ for f in app.log db.log; do tail -F "$f" | sed "s/^/[$f] /" & done; wait

Block headers get messy when logs write simultaneously. A prefix on each line is easier to parse. The & launches each tail in the background, and wait keeps the shell alive.

5. Watch all log files in a directory, including new ones created later

clai
$ clai follow all log files in the directory→ tail -F /var/log/app/*.log

The shell expands the glob once, at startup. Files created after that won't be picked up — you'd have to restart the command.

6. When you need a real interface with panels

clai
$ clai show me several logs in separate panels→ multitail /var/log/syslog /var/log/auth.log

multitail draws panels with syntax highlighting and filters. For incident triage with four logs at once, that's noticeably better than one stream. Install it separately.

Gotchas

  • -f and -F only differ on rotation. Before rotation happens, there's no difference, so the habit of using -f goes unpunished. You get punished the moment you're waiting for a log line during an incident and it stops coming.
  • tail -f over the network doesn't work well. On NFS, changes may not reach the client. Watch the log on the machine where it's being written.
  • Lines can get scrambled. Two processes writing to one stream are not synchronized. To understand event order, read the timestamps inside the lines, not the order of output.

Related questions

How do I start reading from a specific line instead of the tail? tail -n +100 file starts at line 100, tail -c +1000 starts at byte 1000.

How do I follow a log and filter it at the same time? tail -F app.log | grep --line-buffered ERROR. Without --line-buffered, grep buffers the output and lines arrive with lag.

Why is journalctl better than tail for services? It knows about rotation, can filter by unit and log level, and doesn't depend on where the service writes the file.

See also

Stop memorizing tail and logrotate edge cases — describe what you need to see and CliAI writes the command. Install it in one line.