How to Configure Logrotate for Tomcat Access Log Compression Without Rotation


2 views

When running Tomcat servers, the built-in FastCommonAccessLogValve handles basic log rotation but lacks advanced log management features. Many administrators need automated compression and cleanup without interfering with Tomcat's native rotation mechanism.

While find commands in cron jobs work, they create maintenance overhead. Logrotate offers:

  • Centralized configuration for all logs
  • Better error handling and logging
  • Standardized compression options
  • Conditional execution scripts

Here's a working configuration that compresses and cleans Tomcat access logs without rotation:

/var/log/tomcat/access.log {
    # Disable rotation (Tomcat handles this)
    rotate 0
    
    # Compression settings
    compress
    compresscmd /bin/gzip
    compressext .gz
    compressoptions -9
    
    # Cleanup old logs
    maxage 30
    missingok
    
    # Post-compression hook (optional)
    sharedscripts
    postrotate
        # Verify Tomcat is running before touching logs
        if [ -f /var/run/tomcat.pid ]; then
            kill -USR1 cat /var/run/tomcat.pid >/dev/null 2>&1
        fi
    endscript
}

For more control, consider these additional parameters:

# Additional compression optimization
delaycompress
dateext
dateformat -%Y%m%d

# Size-based cleanup (alternative to maxage)
maxsize 100M

1. Save the configuration to /etc/logrotate.d/tomcat
2. Test with debug mode:
logrotate -d /etc/logrotate.d/tomcat
3. Force immediate execution:
logrotate -vf /etc/logrotate.d/tomcat

Check logrotate's own logs for issues:
grep logrotate /var/log/syslog

Common pitfalls include:
- Permission issues (run as correct user)
- SELinux context mismatches
- Disk space monitoring during compression


When working with Tomcat's FastCommonAccessLogValve, we encounter a common infrastructure challenge: while the valve handles daily log rotation effectively, it lacks built-in support for log compression and retention management. This leaves sysadmins with two suboptimal choices:

# Current approach using find
0 2 * * * find /var/log/tomcat/ -name "access_log.*" -mtime +30 -exec gzip {} \;
0 3 * * * find /var/log/tomcat/ -name "access_log.*.gz" -mtime +90 -delete

Using logrotate instead of custom cron jobs offers several advantages:

  • Centralized log management configuration
  • Standardized compression behavior
  • Better integration with system monitoring
  • More sophisticated rotation schemes if needed later

Here's how to configure logrotate to work with Tomcat's existing rotation while adding compression and cleanup:

/var/log/tomcat/access_log.* {
    daily
    missingok
    nodelaycompress
    compress
    delaycompress
    notifempty
    sharedscripts
    postrotate
        /bin/kill -HUP cat /var/run/tomcat.pid 2>/dev/null 2>/dev/null || true
    endscript
    maxage 30
    rotate 0
}

rotate 0: This is the crucial parameter that tells logrotate not to perform any rotation, since Tomcat's valve already handles this.

nodelaycompress + delaycompress: These might seem contradictory but work together - immediate compression of older logs while leaving the most recent one uncompressed.

maxage 30: Automatically removes logs older than 30 days, solving the cleanup requirement.

To test your configuration without waiting for the daily cron:

# Force logrotate to run with debug output
logrotate -d /etc/logrotate.d/tomcat

# Execute a dry run
logrotate -v /etc/logrotate.d/tomcat

For production environments, consider adding these monitoring checks:

# Check if logrotate ran successfully
grep "logrotate" /var/log/syslog | tail -n 5

# Verify compressed files exist
ls -lh /var/log/tomcat/access_log.*.gz

For those preferring a Java solution, you could implement a custom valve extending AccessLogValve:

public class CompressingAccessLogValve extends AccessLogValve {
    @Override
    protected synchronized void close(boolean rename) {
        super.close(rename);
        if (rename) {
            // Add compression logic here
            Path logFile = Paths.get(getDirectory(), getFilename());
            compressLogFile(logFile);
        }
    }
    
    private void compressLogFile(Path logFile) {
        // Implementation using GZIPOutputStream
    }
}

However, this approach requires more maintenance and lacks the system-wide consistency of logrotate.