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.
8 min read
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) |
The /var/log/syslog and /var/log/auth.log files are written by rsyslog. Debian 12 and newer don’t install rsyslog by default, so on those systems the same events are only in the journal (see Using journalctl below) unless you run sudo apt install rsyslog.
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
The most reliable way to filter by time is journalctl, which works the same on every distribution:
# Everything from a specific day
journalctl --since "2026-04-12" --until "2026-04-13"
# The last hour
journalctl --since "1 hour ago"
To grep a syslog file by date, match the timestamp format your system writes. Ubuntu 24.04 and newer start each line with an ISO date, while Rocky/AlmaLinux and older Ubuntu releases use the classic format:
# Ubuntu 24.04+ (lines start with 2026-04-12T14:32:11...)
grep "^2026-04-12" /var/log/syslog
# Rocky/AlmaLinux and older Ubuntu (lines start with "Apr 12"; days 1-9 are space-padded, e.g. "Apr 2")
grep "^Apr 12" /var/log/messages
Using journalctl (systemd)
Modern Linux distributions use systemd-journald as the primary logging system. journalctl is the command for reading and filtering it.
# 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 the slow request log in PHP-FPM (logs a stack trace for slow scripts)
# 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 every HTTP 500 response (status is field 9 of the access log)
tail -F /var/log/nginx/access.log | awk '$9 == 500 {print; fflush()}' | while read -r line; do
echo "$line" | mail -s "500 Error Alert" [email protected]
done
Nginx records the status code of each request in the access log, not the error log, which is why the alert watches access.log. A loop started from your SSH session stops when you log out, so save it as a script and run it as a systemd service, or use a monitoring tool. It also needs a working mail setup for mail to deliver anything.
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 Oct 2026, 00:00 +0300.