Classic tools¶
tail -F /var/log/syslog # follow, survives rotation (-F not -f)
less +F /var/log/messages # follow inside less; Ctrl-C to scroll, F to resume
grep -i 'error' /var/log/syslog
zgrep -i 'error' /var/log/syslog.*.gz # search compressed rotations
zcat /var/log/syslog.2.gz | less
grep -h 'sshd' /var/log/auth.log{,.1} <(zcat /var/log/auth.log.*.gz) # current + rotated
# Count messages per program
awk '{print $5}' /var/log/syslog | sed 's/\[.*//; s/:$//' | sort | uniq -c | sort -rn | head
# Messages per minute (spot bursts)
awk '{print $1, $2, substr($3,1,5)}' /var/log/syslog | uniq -c | sort -rn | head
Use tail -F rather than tail -f so you keep following after logrotate swaps the file.
lnav: the Logfile Navigator¶
lnav is a terminal log viewer that auto-detects common formats (syslog, journald JSON, Apache/nginx access logs, dpkg, and more), merges multiple files into a single timeline, highlights errors, and lets you query logs with SQL. It's the most useful single tool for ad-hoc log investigation on a server.
Install:
sudo apt install lnav # Debian/Ubuntu
sudo dnf install lnav # Fedora; EPEL on RHEL
# Or grab the static binary from the GitHub releases page
Opening logs:
lnav # opens the default system logs
lnav /var/log/syslog /var/log/auth.log # merged by timestamp
lnav /var/log/nginx/ # a whole directory, including .gz rotations
journalctl -o json --since today | lnav # the journal, with fields parsed
lnav user@web01:/var/log/syslog # remote file over SSH
Navigation essentials:
| Key | Action |
|---|---|
e / E |
Next / previous error |
w / W |
Next / previous warning |
/ then n / N |
Search forward, next / previous match |
i |
Toggle histogram view (messages over time by severity) |
p |
Toggle display of parsed fields for the current line |
m |
Bookmark line; u / U jump between bookmarks |
t |
Text view (for non-log files) |
: |
Command prompt (:filter-in, :filter-out, :goto) |
; |
SQL prompt |
? |
Help |
q |
Back / quit |
Filtering:
:filter-in sshd
:filter-out CRON
:goto 2026-10-09 14:00
SQL queries against parsed logs (each format is a table, e.g. syslog_log):
;SELECT log_procname, count(*) AS n FROM syslog_log GROUP BY log_procname ORDER BY n DESC LIMIT 10
;SELECT log_time, log_text FROM syslog_log WHERE log_level = 'error' ORDER BY log_time DESC LIMIT 50
Non-interactive (scriptable) use:
lnav -n -c ';SELECT log_procname, count(*) FROM syslog_log GROUP BY 1 ORDER BY 2 DESC LIMIT 10' /var/log/syslog
lnav also supports custom formats (JSON definitions under ~/.lnav/formats/installed/) for application logs it doesn't recognize.
Other handy tools¶
| Tool | Use |
|---|---|
jq |
Slice JSON logs and journalctl -o json output |
multitail |
Several logs side by side in one terminal |
goaccess |
Real-time web server access log analytics |
logwatch / logcheck |
Daily summary reports by email |
fail2ban |
Watches auth/web logs and bans abusive IPs |
ccze / grc |
Colourize log output |