Apache Log Configuration and Analysis
Apache's logs are essential tools for access pattern analysis, error diagnosis, and security auditing. This chapter covers CustomLog format configuration, ErrorLog levels, and log rotation with rotatelogs — everything needed in production.
Log Types
| Log | Default Path (Ubuntu) | Purpose |
|---|---|---|
| Access Log | /var/log/apache2/access.log | HTTP request records |
| Error Log | /var/log/apache2/error.log | Error and warning records |
CustomLog — Access Log Format
Default Formats
# combined format (most widely used)
LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\"" combined
# common format
LogFormat "%h %l %u %t \"%r\" %>s %O" common
CustomLog ${APACHE_LOG_DIR}/access.log combined
Custom Format with Response Time
# %D: microseconds, %T: seconds
LogFormat "%h %l %u %t \"%r\" %>s %O \"%{Referer}i\" \"%{User-Agent}i\" %D" combined_time
CustomLog ${APACHE_LOG_DIR}/access.log combined_time
JSON Format
LogFormat "{ \
\"time\": \"%{%Y-%m-%dT%H:%M:%S%z}t\", \
\"remote_addr\": \"%a\", \
\"method\": \"%m\", \
\"uri\": \"%U%q\", \
\"status\": %s, \
\"bytes\": %O, \
\"response_time_us\": %D \
}" json
CustomLog ${APACHE_LOG_DIR}/access.json json
Key Log Format Variables
| Variable | Description |
|---|---|
%h | Client IP (or hostname if HostnameLookups is On) |
%a | Client IP (always IP) |
%t | Request time |
%r | Request line (method + URI + protocol) |
%m | Request method |
%U | URL path (no query string) |
%q | Query string |
%>s | Final status code |
%O | Response size including headers (bytes) |
%D | Request processing time (microseconds) |
%T | Request processing time (seconds) |
%{Referer}i | Referer header |
%{User-Agent}i | User-Agent header |
%v | Virtual host name |
ErrorLog Level Settings
ErrorLog ${APACHE_LOG_DIR}/error.log
LogLevel warn
# Levels: emerg > alert > crit > error > warn > notice > info > debug > trace1~8
| Level | Description | For Production |
|---|---|---|
error | Request processing failures | Always logged |
warn | Warnings | Recommended |
info | Informational | Development |
debug | Detailed debug | Temporary only |
Per-Module Log Level
# Overall warn, but trace3 for rewrite module
LogLevel warn rewrite:trace3
Per-VirtualHost Log Separation
<VirtualHost *:80>
ServerName site1.com
ErrorLog ${APACHE_LOG_DIR}/site1.error.log
CustomLog ${APACHE_LOG_DIR}/site1.access.log combined
</VirtualHost>
Excluding Unnecessary Logs
SetEnvIf Request_URI "^/health$" dontlog
SetEnvIf Request_URI "\.(ico|png|jpg|gif|css|js)$" dontlog
CustomLog ${APACHE_LOG_DIR}/access.log combined env=!dontlog
Log Rotation
Using rotatelogs (Apache built-in)
# New log file per day
CustomLog "|/usr/bin/rotatelogs /var/log/apache2/access.%Y%m%d.log 86400" combined
Using logrotate (system tool)
/var/log/apache2/*.log {
daily
missingok
rotate 14
compress
delaycompress
notifempty
sharedscripts
postrotate
if invoke-rc.d apache2 status > /dev/null 2>&1; then
invoke-rc.d apache2 reload > /dev/null 2>&1
fi
endscript
}
Practical Log Analysis
# Status code distribution
awk '{print $9}' /var/log/apache2/access.log | sort | uniq -c | sort -rn
# Top 20 most requested URLs
awk '{print $7}' /var/log/apache2/access.log | sort | uniq -c | sort -rn | head -20
# Top 10 IPs by request count
awk '{print $1}' /var/log/apache2/access.log | sort | uniq -c | sort -rn | head -10
# All 5xx error requests
awk '($9 ~ /^5/) {print $1, $7, $9}' /var/log/apache2/access.log
Summary
| Item | Directive | Recommendation |
|---|---|---|
| Access log | CustomLog | combined with response time |
| Error log | ErrorLog + LogLevel | Production: warn, debug: per-module |
| Exclude noise | SetEnvIf + env=! | Exclude health checks, static files |
| Rotation | logrotate | daily + 14 days + compress |
| Multiple sites | Per-VirtualHost logs | Separate logs per site |