#!/usr/bin/perl -w # # RCS ID: $Revision: 1.7 $ # # Script to rotate the specified logs and name them with yesterday's # date. The script also restarts the web server with a specified command. # and optionally compresses old logs and removes them after a certain period. # # Copyright (c) 2002, 2003, 2004 Urchin Software Corporation # # Usage: weblog_rotate.pl [--log /path/to/log [--log /path/to/log] \ # | [--loglist /path/to/loglist]] \ # --touchlog \ # --restart_cmd "apachectl restart" \ # --compress --days N --help # # Where: # --loglist specifies the path to a file containing the full pathnames # of all the logs that are to be rotated. This option cannot be used # with the --log option. # --log specifies the full path to the log file to be rotated # (multiple --log entries are allowed) # --touchlog specifies that a new log should be created once the # original log is rotated. # (default is not to create the log) # --restart_cmd specifies the command to restart the web server # (default is apachectl restart) # --compress specifies whether or not to compress the logs with gzip # (default is off) # --noprune disables automatic pruning of archived logs # (default is off) # --days specifies how many days to keep archived logs # (default is 60, minimum value is 1) # --help shows this message\n\n"; use strict; use Time::localtime; use Getopt::Long; # Initialize variables my @logs = (); my $loglistfile = ""; my $restart_cmd = "apachectl restart"; my $days = 60; my $compress = 0; my $noprune = 0; my $touchlog = 0; my $gstatus; my $abslogpath; my $help = 0; # Read in the options $gstatus = GetOptions('loglist=s' => \$loglistfile, 'log=s' => \@logs, 'restart_cmd=s' => \$restart_cmd, 'noprune' => \$noprune, 'days=i' => \$days, 'touchlog' => \$touchlog, 'compress' => \$compress, 'help' => \$help); if ($help) { &Usage; exit; } if ($gstatus ne 1) { &Usage; exit(1); } if ( $loglistfile ne "" ) { if ( @logs ) { print "FATAL error: \"--log\" and \"--loglist\" options are mutually exclusive\n"; &Usage; exit(1); } open(LOGLISTFILE, $loglistfile) || die "FATAL error: can't open log list file $loglistfile\n"; @logs = ; } # Calculate the date string for 'yesterday' my $yesterday = convert_epoch(time() - 86400); # Ensure that '$days' is at least 1 if ($days <= 0) { $days = 1; } # Calculate the date string for '$days' ago my $cutoff_date = convert_epoch(time() - ($days * 86400)); # Rotate each log file inside its current directory my ($log, $new_log, $dir); my ($mode, $uid, $gid); foreach $log (@logs) { chomp($log); # Create the name for the new log $new_log = "$log.$yesterday"; # Verify that $log exists and is readable if ((!-e $log) && (!-r $log)) { warn "Logfile: $log does not exist or is unreadable\n"; next; } # Move $log to $new_log as long as it doesn't overwrite another file if ((-e $new_log) || ((-e "$new_log.gz") && $compress)) { warn "Logfile: $new_log already exists. Skipping rotation...\n"; } else { rename($log, $new_log) || warn "Unable to rename: $log to $new_log"; if ( $touchlog ) { ($mode,$uid,$gid) = (stat("$new_log"))[2,4,5]; open(LOG,"+>$log") || warn "Unable to create $log"; close(LOG); chown($uid,$gid,$log) || warn "Unable to change ownership/group of $log to UID $uid/GID $gid"; chmod($mode,$log) || warn "Unable to change permissions of $log to $mode"; } } } # Restart the web server if ($restart_cmd) { system($restart_cmd) && warn "$restart_cmd failed to restart web server properly"; } # Loop through logs and prune and/or gzip as necessary. foreach $log (@logs) { # Create the name for the new log $new_log = "$log.$yesterday"; # Determine the directory where the log file is located $_ = $log; ($dir) = m#(^/.*/)#; # Compress the logs if set as option if ($compress) { # Verify that $new_log exists and is readable and compress the log if ((!-e $new_log) && (!-r $new_log)) { warn "Cannot compress $new_log since it does not exist or is not readable"; next; } else { system("/usr/bin/gzip $new_log") && warn "Problems gzipping $new_log"; } } # Go to the next log if log pruning is disabled if ($noprune) { print "Skipping log pruning for $log\n"; next; } # Remove outdated logs from the directory opendir(DIR, $dir) || warn "Unable to open archived logs directory: $dir"; while (my $file = readdir(DIR)) { $abslogpath = "$dir$file"; # Skip logs that don't match the same base logfile name if ( index($abslogpath,$log) == -1 ) { next; } # Skip remaining logs that don't have a YYYYMMDD suffix pattern if ($file =~ /(\d{8})/) { if ($1 < $cutoff_date) { unlink("$dir$file") || warn "Problems removing file: $dir$file"; } } } } # Subroutine to show the usage of this script sub convert_epoch { my $rawtime = $_[0]; my $formatedtime = ""; if ((! defined($rawtime)) || $rawtime eq "") { $formatedtime = 0; } else { $rawtime = localtime($rawtime); $formatedtime = sprintf("%04d%02d%02d", $rawtime->year+1900, $rawtime->mon+1, $rawtime->mday); } return($formatedtime); } sub Usage { print "Usage: $0 [--loglist /path/to/loglist || --log /path/to/log [--log /path/to/log] ] \\ --touchlog \\ --restart_cmd \"apachectl restart\" \\ --compress [--noprune | --days N] --help Where: --loglist specifies the path to a file containing the full pathnames of all the logs that are to be rotated. This option cannot be used with the --log option. --log specifies the full path to the log file to be rotated (multiple --log entries are allowed) --touchlog specifies that a new log should be created once the original log is rotated. (default is not to create the log) --restart_cmd specifies the command to restart the web server (default is apachectl restart) --compress specifies whether or not to compress the logs with gzip (default is off) --noprune disables automatic pruning of archived logs (default is off) --days specifies how many days to keep archived logs (default is 60, minimum value is 1) --help shows this message\n\n"; }