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 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 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 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 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 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 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
-fand-Fonly differ on rotation. Before rotation happens, there's no difference, so the habit of using-fgoes unpunished. You get punished the moment you're waiting for a log line during an incident and it stops coming.tail -fover 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
- Read a service's logs with journalctl
- Sweep old logs and backups with one sentence
- Grep with exclusions
Stop memorizing tail and logrotate edge cases — describe what you need to see and CliAI writes the command. Install it in one line.