10.4 tail -f: Following Growing Log Files in Real Time
Alright, let’s talk about one of the most satisfyingly simple yet powerful tools in your arsenal: tail -f. This is the command you run when you need to watch a log file grow in real time, like a live commentary on the chaotic, often nonsensical, performance of your application. It’s the sysadmin’s equivalent of turning on the commentary track for a disaster movie.
The Basic Incantation
At its heart, it’s brutally simple. You’re telling tail: “Show me the last 10 lines of this file, and then don’t quit. Stick around. Keep showing me whatever new stuff gets appended to it.”
tail -f /var/log/syslog
Now, as your application vomits its thoughts into that log file, you’ll see each new line pop onto your screen. It’s perfect for watching request logs during a load test, debugging a service that’s misbehaving, or just feeling like a wizard watching streams of data flow by. The -f flag stands for “follow,” which is admirably straightforward for a Unix tool.
Why It Works (And Why It Sometimes Doesn’t)
tail -f works because it doesn’t just read the file once and exit. It opens the file, sits in a loop, and periodically checks to see if the file’s size has increased. If it has, it reads the new bytes and prints them out. This is why it’s so laser-focused on the end of the file; it simply doesn’t care about anything that happened before its watch began.
But here’s the first pitfall, the one that has caused many a junior admin to question their sanity: log rotation.
Most logging systems are smart enough to not let a single log file consume the entire disk. They use a process called rotation: when logfile.log gets too big, it’s renamed to logfile.log.1, and a new, empty logfile.log is created. Your tail -f process is still patiently watching the file descriptor of the original file, which is now called logfile.log.1. It will dutifully show you that nothing is ever written to that file again, while you stare blankly at a motionless screen, wondering why your application has suddenly gone silent. It hasn’t. The logs are now being written to the new file, and you’re watching the old one.
The designers of tail eventually acknowledged this absurdity and gave us a better option.
The Smarter Sibling: –follow=name
To handle the log rotation issue, you need the --follow=name option. This tells tail to be a bit less naive. Instead of blindly following the file descriptor it initially opened, it periodically checks the filename you gave it to see if it’s still the same file (specifically, the same inode). If a rotation happens and the original file is moved away and a new one is created with the same name, tail will notice, close the old file, and open the new one.
tail --follow=name /var/log/nginx/access.log
This is what you should almost always use for following log files. It’s the robust way to do it. The default -f (which is aliased to --follow=descriptor) is really only useful if you’re following a file that you know won’t be renamed or recreated, like a custom output stream you’ve set up yourself.
Following Multiple Files and Other Niceties
Want to feel like you’re at mission control? You can follow multiple files at once. The output is interleaved, with a header telling you which file each new line came from.
tail -f /var/log/service1.log /var/log/service2.log
It’s chaotic, but brilliantly useful for watching closely related services. You can also change the number of initial lines it shows with -n. To see the last 50 lines and then follow, you’d use:
tail -n 50 -f /var/log/super_verbose.log
When to Break Out of Tail
Sometimes, the firehose of data from tail -f is too much. You need to filter it. The classic move here is to pipe the output into grep to only see the lines you care about.
tail -f /var/log/auth.log | grep "Failed password"
A crucial warning here: Buffering will be the death of you. The standard output (stdout) of tail is typically buffered when piped to another command, which can introduce a frustrating delay. You might sit there for seconds (an eternity) waiting for your filtered lines to appear. To fix this, you can use the --line-buffered option in grep to force it to flush its output after every line.
tail -f /var/log/auth.log | grep --line-buffered "Failed password"
This gives you real-time, filtered monitoring. It’s a beautiful thing.
So there you have it. Use --follow=name for logs. Pipe to grep --line-buffered when you need to. And never assume that a motionless tail -f means a quiet system—it might just mean you got rotated out.