How to Automatically Gzip and Archive Rotated Apache Logs Created by rotatelogs


4 views

When using Apache's rotatelogs to create daily log files in the format host.DD.MM.YYYY.access.log, we often need to compress these logs after rotation and move them to an archive directory. The native rotatelogs doesn't provide built-in compression functionality.

Here are three effective methods to handle this:

Create a wrapper script that handles both rotation and compression:

#!/bin/bash
LOGDIR="/var/log/apache2"
ARCHIVEDIR="/var/log/apache2/archive"
LOGNAME="host"

# Rotate and compress
/usr/sbin/rotatelogs -l ${LOGDIR}/${LOGNAME}.%d.%m.%Y.access.log 86400 | \
while read line; do
    # When rotatelogs creates a new file, compress the previous one
    if [[ $line =~ "opening new log file" ]]; then
        PREV_FILE=$(echo $line | awk '{print $NF}')
        gzip -9 "$PREV_FILE" && \
        mv "${PREV_FILE}.gz" "$ARCHIVEDIR"
    fi
done

Configure logrotate to handle the compressed archives:

/var/log/apache2/host.*.access.log {
    daily
    missingok
    rotate 0
    compress
    delaycompress
    notifempty
    create 0640 root adm
    sharedscripts
    postrotate
        /usr/sbin/apachectl graceful
    endscript
    lastaction
        mv /var/log/apache2/host.*.access.log.gz /var/log/apache2/archive/
    endaction
}

For more real-time processing, use inotify-tools:

#!/bin/bash
LOGDIR="/var/log/apache2"
ARCHIVEDIR="/var/log/apache2/archive"

inotifywait -m -e close_write --format '%w%f' "$LOGDIR" | \
while read file; do
    if [[ $file =~ host\.[0-9]{2}\.[0-9]{2}\.[0-9]{4}\.access\.log$ ]]; then
        gzip -9 "$file" && \
        mv "${file}.gz" "$ARCHIVEDIR"
    fi
done

When implementing any of these solutions:

  • Ensure proper file permissions are maintained
  • Consider adding error handling for the move operation
  • Monitor disk space in the archive directory
  • Test with small rotation intervals first

For maximum control, consider writing a custom log rotator:

#!/usr/bin/perl
use strict;
use warnings;
use POSIX qw(strftime);
use IO::Compress::Gzip qw(gzip $GzipError);

my $logdir = '/var/log/apache2';
my $archivedir = '/var/log/apache2/archive';
my $logname = 'host';
my $current_file;

while () {
    my $today = strftime("%d.%m.%Y", localtime);
    unless ($current_file && $current_file =~ /$today/) {
        close LOG if $current_file;
        $current_file = "$logdir/$logname.$today.access.log";
        open LOG, '>>', $current_file or die $!;
        
        # Compress yesterday's log
        if (my @yesterdays = glob("$logdir/$logname.*.access.log")) {
            foreach my $file (@yesterdays) {
                next if $file eq $current_file;
                my $gzfile = "$file.gz";
                gzip $file => $gzfile or die "gzip failed: $GzipError";
                system('mv', $gzfile, $archivedir) == 0 or warn "mv failed: $?";
            }
        }
    }
    print LOG $_;
}

When using Apache's rotatelogs utility, logs are typically created in daily files with patterns like host.DD.MM.YYYY.access.log. While rotation happens automatically, we often need to compress and archive these logs to save disk space. Here's a comprehensive solution.

The most reliable approach is to use a small script that gets executed after each rotation. Modify your Apache configuration:


CustomLog "|/usr/sbin/rotatelogs -l /var/log/apache2/host.%d.%m.%Y.access.log 86400" combined

Then create a post-rotation script (/usr/local/bin/compress_logs.sh):


#!/bin/bash

# Directory where logs are being rotated to
LOG_DIR="/var/log/apache2"
ARCHIVE_DIR="/var/log/apache2/archives"

# Find yesterday's log (most recently rotated)
YESTERDAY=$(date -d "yesterday" +"%d.%m.%Y")
LOG_FILE="${LOG_DIR}/host.${YESTERDAY}.access.log"

# Compress and move if file exists
if [ -f "$LOG_FILE" ]; then
    gzip -9 "$LOG_FILE"
    mkdir -p "$ARCHIVE_DIR"
    mv "${LOG_FILE}.gz" "$ARCHIVE_DIR/"
fi

For more control, set up a daily cron job that runs shortly after midnight:


# Daily at 12:05 AM
5 0 * * * /usr/local/bin/compress_logs.sh

For real-time compression, you can pipe directly to gzip:


CustomLog "|/usr/sbin/rotatelogs -l /var/log/apache2/host.%d.%m.%Y.access.log 86400 | gzip -9 > /var/log/apache2/archives/host.%d.%m.%Y.access.log.gz" combined

For environments with many virtual hosts, use this enhanced script:


#!/bin/bash
LOG_DIR="/var/log/apache2"
ARCHIVE_DIR="/var/log/apache2/archives"
DAYS_TO_KEEP=30

# Process all yesterday's logs
for log in $(find "$LOG_DIR" -name "host.$(date -d "yesterday" +"%d.%m.%Y").*.log"); do
    gzip -9 "$log"
    mkdir -p "$ARCHIVE_DIR"
    mv "${log}.gz" "$ARCHIVE_DIR/"
done

# Cleanup old archives
find "$ARCHIVE_DIR" -name "host.*.log.gz" -mtime +$DAYS_TO_KEEP -delete

Always test your compression script manually first. Consider adding logging to your script:


echo "$(date) - Compressed ${LOG_FILE} to ${ARCHIVE_DIR}/" >> /var/log/log_compression.log