Where Logs Reside
Almost everything that happens on a Linux system is logged. Traditionally, logs are flat text files in /var/log. Modern systems also use the systemd journal (binary format, queried with journalctl).
# Browse the log directory
$ ls -la /var/log/
total 216
drwxr-xr-x 3 root root 4096 Apr 10 08:00 .
drwxr-xr-x 3 root root 4096 Apr 10 08:00 ..
drwxr-xr-x 3 root root 4096 Apr 10 08:00 apt/ ← Package manager history
drwxr-xr-x 2 root root 4096 Apr 10 09:05 auth.log ← Authentication attempts
drwxr-xr-x 3 root root 4096 Apr 10 08:00 boot.log ← Boot messages
drwxr-xr-x 3 root root 4096 Apr 10 08:00 btmp ← Failed login attempts (binary)
drwxr-xr-x 3 root root 4096 Apr 10 09:05 daemon.log ← Daemon messages
drwxr-xr-x 3 root root 4096 Apr 10 08:00 dmesg ← Kernel ring buffer
drwxr-xr-x 3 root root 4096 Apr 10 08:00 dpkg.log ← Package installation
drwxr-xr-x 3 root root 4096 Apr 10 09:05 kern.log ← Kernel messages
drwxr-xr-x 3 root root 4096 Apr 10 08:00 syslog ← System events
drwxr-xr-x 3 root root 4096 Apr 10 08:00 nginx/ ← Web server logs
drwxr-xr-x 3 root root 4096 Apr 10 08:00 apache2/ ← Apache logs
drwxr-xr-x 3 root root 4096 Apr 10 08:00 docker/ ← Container logs
drwxr-xr-x 3 root root 4096 Apr 10 08:00 journal/ ← Systemd journal (binary)Important Log Files
| File | Contents | When to Check |
|---|---|---|
/var/log/auth.log | SSH logins, sudo, user authentication | Security auditing, brute force detection |
/var/log/syslog | General system events, cron, services | General troubleshooting |
/var/log/kern.log | Kernel messages, hardware events | Hardware issues, driver problems |
/var/log/dmesg | Boot-time kernel messages | Driver loading, hardware detection |
/var/log/nginx/access.log | All HTTP requests to NGINX | Traffic analysis, 404/500 errors |
/var/log/nginx/error.log | NGINX errors and warnings | Web server problems |
/var/log/apache2/access.log | Apache HTTP requests | Same as NGINX access |
/var/log/docker/daemon.log | Docker daemon messages | Container infrastructure issues |
/var/log/apt/history.log | Package installations/removals | System changes |
/var/log/btmp | Failed login attempts | Security — who's trying to break in? |
/var/log/wtmp | Successful login/logout history | Who logged in and when |
Reading Log Files
cat — Entire File
# Display entire log (works for small files)
$ cat /var/log/auth.log | head -50
Apr 10 08:00:01 server sshd[1234]: Server listening on 0.0.0.0 port 22.
Apr 10 08:15:23 server sshd[5678]: Accepted publickey for admin from 192.168.1.50
Apr 10 08:15:23 server systemd-logind[890]: New session 12 of user admin.head and tail — Beginning and End
# Show first 20 lines
$ head -20 /var/log/syslog
# Show last 20 lines (most recent entries)
$ tail -20 /var/log/syslog
Apr 10 11:45:00 server CRON[9876]: (root) CMD (test -x /usr/sbin/anacron)
Apr 10 11:45:00 server CRON[9876]: (root) CMD (test -x /usr/sbin/run-parts /etc/cron.hourly)
Apr 10 11:47:00 server systemd[1]: Started Session 15 of user admin.tail -f — Live Tail (Real-Time Monitoring)
# Watch logs in real-time — essential for troubleshooting
$ sudo tail -f /var/log/nginx/access.log
192.168.1.50 - - [10/Apr/2026:11:47:00 +0000] "GET /api/health HTTP/1.1" 200 15 "-" "curl/7.81.0"
192.168.1.51 - - [10/Apr/2026:11:47:05 +0000] "GET /api/users HTTP/1.1" 500 342 "-" "python-requests/2.28"
192.168.1.50 - - [10/Apr/2026:11:47:10 +0000] "GET /api/health HTTP/1.1" 200 15 "-" "curl/7.81.0"
^C
# Tail multiple files at once
$ sudo tail -f /var/log/nginx/access.log /var/log/nginx/error.log# Tail with line count
$ tail -n 100 /var/log/syslog
# Shows last 100 linesFiltering Logs with grep
# Find all ERROR entries
$ grep ERROR /var/log/app.log
2026-04-10 09:15:23 ERROR Database connection failed
2026-04-10 09:16:45 ERROR Request timeout: upstream read timeout
# Find entries from a specific time
$ grep "2026-04-10 10:" /var/log/app.log
# Find errors with context (5 lines before and after)
$ grep -A 5 -B 5 "ERROR" /var/log/app.log
# Count errors by type
$ grep ERROR /var/log/app.log | awk '{print $4}' | sort | uniq -c
12 ConnectionError
5 TimeoutError
3 ValidationError
# Exclude DEBUG lines
$ grep -v DEBUG /var/log/app.log
# Use case-insensitive search
$ grep -i "warning\|error\|critical" /var/log/syslogjournalctl — Systemd Journal
Systemd stores logs in binary format in /var/log/journal/. Query them with journalctl.
Basic Usage
# Show all logs (opens in pager, like less)
$ journalctl
# View logs for a specific service
$ journalctl -u nginx.service
# Follow logs in real-time
$ journalctl -u nginx.service -f
# View since last boot
$ journalctl -b
# View logs from previous boot (if available)
$ journalctl -b -1
# Show last N lines
$ journalctl -u nginx.service -n 50
# Follow the system journal (all services)
$ journalctl -fFiltering by Priority
Systemd uses log levels (similar to syslog):
| Level | Name | Severity | When to Use |
|---|---|---|---|
| 0 | emerg | System unusable | Emergency only |
| 1 | alert | Must act immediately | Immediate action needed |
| 2 | crit | Critical | Critical failures |
| 3 | err | Error | Errors |
| 4 | warning | Warning | Warnings |
| 5 | notice | Notice | Significant but normal |
| 6 | info | Informational | Normal info |
| 7 | debug | Debug | Debug messages |
# Show only errors and worse
$ journalctl -p err
# Show warnings and above (errors, alerts, emergencies)
$ journalctl -p warning
# Combine with service
$ journalctl -u nginx.service -p errTime-Based Queries
# Logs since specific time
$ journalctl --since "2026-04-10 09:00:00"
$ journalctl --since "1 hour ago"
$ journalctl --since "today"
$ journalctl --since "yesterday"
# Logs between two times
$ journalctl --since "2026-04-10 09:00:00" --until "2026-04-10 10:00:00"
# All logs since midnight
$ journalctl --since todayDisk Usage
# Check journal disk usage
$ journalctl --disk-usage
Archived and active journals take up 1.2G on disk.
# Limit journal size (in /etc/systemd/journald.conf)
# Runtime journal max size
journald.conf: SystemMaxUse=500MNGINX Log Format
Understanding log formats is essential for parsing them.
Default NGINX Access Log
$ tail -5 /var/log/nginx/access.log
192.168.1.50 - admin [10/Apr/2026:11:47:00 +0000] "GET /api/health HTTP/1.1" 200 15 "-" "curl/7.81.0"
192.168.1.51 - - [10/Apr/2026:11:47:05 +0000] "POST /api/users HTTP/1.1" 201 45 "-" "python-requests/2.28"
192.168.1.50 - admin [10/Apr/2026:11:47:10 +0000] "GET /api/health HTTP/1.1" 200 15 "-" "curl/7.81.0"
Breakdown:
IP User Time Request Status Size Referrer User-Agent
192.168.1.50 admin 10/Apr/2026:11:47:00 +0000 "GET /api/health HTTP/1.1" 200 15 "-" "curl/7.81.0"
Parsing NGINX Logs
# Top 10 slowest requests (by response time, if logged)
$ awk '{print $NF}' /var/log/nginx/access.log | sort -rn | head
# Error rate (non-200 responses)
$ grep -v '" 200 ' /var/log/nginx/access.log | wc -l
# Requests per minute
$ awk '{print substr($4, 14, 5)}' /var/log/nginx/access.log | sort | uniq -c
# Unique IP addresses
$ awk '{print $1}' /var/log/nginx/access.log | sort -u | wc -lLog Rotation
Logs grow indefinitely — log rotation prevents them from filling the disk.
# View logrotate configuration
$ cat /etc/logrotate.conf
# View nginx logrotate config
$ cat /etc/logrotate.d/nginx
/var/log/nginx/*.log {
daily # Rotate daily
missingok # Don't error if file is missing
rotate 14 # Keep 14 rotated files
compress # Compress old logs
delaycompress # Delay compression (keep 1 uncomp)
notifempty # Don't rotate if empty
create 0640 www-data adm # New log file permissions
sharedscripts # Run postrotate script once
postrotate
invoke-rc.d nginx rotate >/dev/null 2>&1
endscript
}Quick Reference
| Task | Command |
|---|---|
| Read entire log | cat /var/log/syslog |
| First 50 lines | head -50 /var/log/syslog |
| Last 50 lines | tail -50 /var/log/syslog |
| Live tail | tail -f /var/log/syslog |
| Search for errors | grep ERROR /var/log/app.log |
| Service logs | journalctl -u <service> |
| Follow service logs | journalctl -u <service> -f |
| Logs since boot | journalctl -b |
| Error logs only | journalctl -p err |
| Logs since time | journalctl --since "1 hour ago" |
Practice Challenge
- Run
tail -f /var/log/syslogand watch system events in real-time (press Ctrl+C to exit) - Find all entries in
/var/log/auth.logrelated to sudo usage:grep sudo /var/log/auth.log - Check the size of
/var/log/withdu -sh /var/log/ - View the last 20 entries in the nginx (or apache) access log if it exists
- Run
journalctl -u ssh— what SSH events are logged? - Find failed login attempts:
journalctl -p error check/var/log/btmpwithlastb