How to Read and Analyze Server Logs
Find, read, filter, and manage logs on your GoZen VPS. Covers syslog, journalctl, Nginx, Apache, PHP-FPM, MySQL, and Fail2Ban logs.
Logs tell you exactly what’s happening on your GoZen VPS. When a site goes down, a service crashes, or an attacker probes your ports, the answer is in the logs. This guide shows you where to look, what to look for, and how to work with logs efficiently.
Where Logs Live
| Log File | What It Tracks |
|---|---|
/var/log/syslog | General system events (Ubuntu/Debian) |
/var/log/messages | General system events (Rocky/AlmaLinux) |
/var/log/auth.log | SSH logins, sudo usage, authentication (Ubuntu/Debian) |
/var/log/secure | SSH logins, sudo usage, authentication (Rocky/AlmaLinux) |
/var/log/nginx/access.log | Every HTTP request to Nginx |
/var/log/nginx/error.log | Nginx errors (misconfigs, upstream failures) |
/var/log/apache2/access.log | Every HTTP request to Apache (Ubuntu/Debian) |
/var/log/httpd/access_log | Every HTTP request to Apache (Rocky/AlmaLinux) |
/var/log/apache2/error.log | Apache errors (Ubuntu/Debian) |
/var/log/httpd/error_log | Apache errors (Rocky/AlmaLinux) |
/var/log/mysql/error.log | MySQL/MariaDB errors and warnings |
/var/log/php*-fpm.log | PHP-FPM errors (pool issues, crashes) |
/var/log/fail2ban.log | Fail2Ban bans and unbans |
/var/log/mail.log | Mail server activity (Postfix, Dovecot) |
/var/log/cron | Cron job execution (Rocky/AlmaLinux) |
Basic Log Commands
View a Log File
# Print the entire log (not recommended for large files)
cat /var/log/syslog
# View the last 50 lines
tail -50 /var/log/syslog
# Follow a log in real time (Ctrl+C to stop)
tail -f /var/log/syslog
# Follow multiple logs simultaneously
tail -f /var/log/nginx/access.log /var/log/nginx/error.log
# Scroll through a log interactively (press q to quit)
less /var/log/syslog
# Tip: press Shift+F in less to follow like tail -f
Search Within Logs
# Find lines containing a keyword
grep "error" /var/log/syslog
# Case-insensitive search
grep -i "failed" /var/log/auth.log
# Show 3 lines before and after each match (context)
grep -B3 -A3 "OOM" /var/log/syslog
# Count occurrences
grep -c "Failed password" /var/log/auth.log
# Search across multiple log files
grep -r "502 Bad Gateway" /var/log/nginx/
# Exclude patterns
grep -v "GET /health" /var/log/nginx/access.log
Filter by Time
# Lines from a specific date
grep "Apr 12" /var/log/syslog
# Lines from the last hour (using awk with timestamps)
awk -v d="$(date -d '1 hour ago' '+%b %d %H')" '$0 >= d' /var/log/syslog
Using journalctl (systemd)
Modern Linux distributions use systemd-journald as the primary logging system. journalctl is its powerful query tool.
# View all logs (newest last)
journalctl
# Follow in real time
journalctl -f
# Logs from a specific service
journalctl -u nginx
journalctl -u mysql
journalctl -u php8.2-fpm
# Logs from the last hour
journalctl --since "1 hour ago"
# Logs from a specific time range
journalctl --since "2026-04-12 14:00" --until "2026-04-12 16:00"
# Logs since last boot
journalctl -b
# Only error messages and above
journalctl -p err
# Kernel messages only
journalctl -k
# Show logs in reverse (newest first)
journalctl -r
# Output as JSON (useful for parsing)
journalctl -u nginx --output=json-pretty --since "1 hour ago"
Priority Levels
The -p flag filters by severity:
| Level | Name | What It Means |
|---|---|---|
| 0 | emerg | System is unusable |
| 1 | alert | Action must be taken immediately |
| 2 | crit | Critical conditions |
| 3 | err | Error conditions |
| 4 | warning | Warning conditions |
| 5 | notice | Normal but significant events |
| 6 | info | Informational messages |
| 7 | debug | Debug-level messages |
# Show everything from "warning" and above
journalctl -p warning
# Show only critical and emergency
journalctl -p crit
Analyzing Web Server Logs
Nginx / Apache Access Log Format
A typical access log line looks like this:
203.0.113.50 - - [12/Apr/2026:14:32:11 +0000] "GET /wp-login.php HTTP/1.1" 200 4523 "-" "Mozilla/5.0..."
| Field | Meaning |
|---|---|
203.0.113.50 | Client IP address |
[12/Apr/2026:14:32:11 +0000] | Timestamp |
GET /wp-login.php HTTP/1.1 | Request method, path, and protocol |
200 | HTTP status code |
4523 | Response size in bytes |
Mozilla/5.0... | User agent string |
Useful One-Liners
# Top 20 IPs by request count
awk '{print $1}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
# Top 20 most requested URLs
awk '{print $7}' /var/log/nginx/access.log | sort | uniq -c | sort -rn | head -20
# All 404 errors
awk '$9 == 404' /var/log/nginx/access.log
# All 500 errors (server errors)
awk '$9 == 500' /var/log/nginx/access.log
# Requests per hour (traffic pattern)
awk '{print $4}' /var/log/nginx/access.log | cut -d: -f1-2 | sort | uniq -c
# Requests to wp-login.php (potential brute force)
grep "wp-login.php" /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -10
# Bandwidth usage per IP (top consumers)
awk '{ip[$1]+=$10} END {for (i in ip) print ip[i], i}' /var/log/nginx/access.log | sort -rn | head -20
# All requests from a specific IP
grep "203.0.113.50" /var/log/nginx/access.log
Identify Attacks and Abuse
# Brute force attempts on SSH
grep "Failed password" /var/log/auth.log | awk '{print $(NF-3)}' | sort | uniq -c | sort -rn | head -10
# WordPress login brute force
grep "POST /wp-login.php" /var/log/nginx/access.log | awk '{print $1}' | sort | uniq -c | sort -rn | head -10
# Suspicious URL patterns (SQL injection, path traversal)
grep -E "(union|select|\.\.\/|etc\/passwd|<script)" /var/log/nginx/access.log
# Bot traffic
grep -i "bot\|crawler\|spider" /var/log/nginx/access.log | wc -l
# Check who Fail2Ban has blocked
sudo fail2ban-client status sshd
grep "Ban" /var/log/fail2ban.log | tail -20
Analyzing PHP-FPM Logs
PHP-FPM logs reveal application crashes, memory issues, and timeout problems:
# Find the PHP-FPM log file
ls /var/log/php*
# Common error patterns
grep "FATAL" /var/log/php8.2-fpm.log
grep "WARNING" /var/log/php8.2-fpm.log
grep "child" /var/log/php8.2-fpm.log # process crashes
grep "memory" /var/log/php8.2-fpm.log # memory exhaustion
# Enable slow query logging in PHP-FPM
# Edit your pool config (e.g., /etc/php/8.2/fpm/pool.d/www.conf)
# slowlog = /var/log/php8.2-fpm-slow.log
# request_slowlog_timeout = 5s
Analyzing MySQL / MariaDB Logs
# Error log (crashes, startup issues)
tail -50 /var/log/mysql/error.log
# Enable and view the slow query log
# In /etc/mysql/mysql.conf.d/mysqld.cnf:
# slow_query_log = 1
# slow_query_log_file = /var/log/mysql/slow.log
# long_query_time = 2
# View slow queries
tail -50 /var/log/mysql/slow.log
# Most frequently slow queries
mysqldumpslow -s c -t 10 /var/log/mysql/slow.log
Live Monitoring
Watch Multiple Logs at Once
# Using multitail (install it first)
sudo apt install multitail -y # Ubuntu/Debian
sudo dnf install multitail -y # Rocky/AlmaLinux
# Watch Nginx access and error logs side by side
multitail /var/log/nginx/access.log /var/log/nginx/error.log
# Mix system and application logs
multitail /var/log/syslog /var/log/nginx/error.log /var/log/mysql/error.log
Real-Time HTTP Status Monitoring
# Watch HTTP status codes in real time
tail -f /var/log/nginx/access.log | awk '{print $9}' | sort | uniq -c
# Color-coded real-time log viewer
tail -f /var/log/nginx/access.log | \
awk '{
if ($9 >= 500) printf "\033[31m%s\033[0m\n", $0; # Red for 5xx
else if ($9 >= 400) printf "\033[33m%s\033[0m\n", $0; # Yellow for 4xx
else if ($9 >= 300) printf "\033[36m%s\033[0m\n", $0; # Cyan for 3xx
else printf "\033[32m%s\033[0m\n", $0; # Green for 2xx
}'
Log Management Best Practices
Set up log rotation - Don’t let logs grow forever. See our Disk Management Guide for logrotate configuration.
Keep logs for at least 14 days - You need enough history to diagnose recurring issues, but not so much that it fills your disk.
Limit journal size - Add
SystemMaxUse=200Mto/etc/systemd/journald.conf.Centralize logs for critical servers - For production environments, consider forwarding logs to a remote syslog server or a service like Grafana Loki.
Monitor, don’t just read - Set up alerts for critical patterns:
# Simple alert: email on any 500 error
tail -F /var/log/nginx/error.log | grep --line-buffered "500" | while read line; do
echo "$line" | mail -s "500 Error Alert" admin@yourdomain.com
done &
Troubleshooting
| Problem | Fix |
|---|---|
| Log file is empty | The service may log to journal instead. Check with journalctl -u servicename. |
| Log file doesn’t exist | The service may not be installed, or the log path is different. Check the service config for the error_log or ErrorLog directive. |
| Logs are too big to open | Use tail, less, or grep instead of cat. For analysis, use awk one-liners. |
| Can’t read logs - permission denied | Most logs require root. Use sudo. |
| Old logs are missing | Log rotation is removing them. Check /etc/logrotate.d/ for retention settings. |
journalctl shows very little | The journal may not persist across reboots. Run sudo mkdir -p /var/log/journal && sudo systemctl restart systemd-journald to enable persistence. |
| Nginx access log shows internal IPs (127.0.0.1) | You’re behind a reverse proxy or load balancer. Configure real_ip in Nginx to log the client’s actual IP. |
Related Articles
Last updated 21 Apr 2026, 08:08 +0300.