#!/usr/bin/perl -w # # $Id: replay-log,v 1.9 2005/08/06 21:00:00 jmates Exp $ # # The author disclaims all copyrights and releases this script into the # public domain. # # Replays logs, simulating the time delays present in the source # data. Only supports the standard Unix syslogd(8) format at present. # # Run perldoc(1) on this script for additional documentation. use POSIX qw(strftime); use Time::Local qw(timelocal); use Time::HiRes qw(sleep); use Getopt::Std; my %opts; getopts 'h?r:f:o:s:', \%opts; print_help() if exists $opts{h} or exists $opts{'?'}; my ( $previous_epoch, $random_seed, $speed_factor ); # increase playback speed if ( exists $opts{f} and defined $opts{f} and $opts{f} > 1 ) { $speed_factor = $opts{f}; } else { $speed_factor = 1; } # use random seed to determine delay for next log entry if ( exists $opts{r} and defined $opts{r} and $opts{r} > 0 ) { $random_seed = $opts{r}; $previous_epoch = time; } # use custom output file if ( exists $opts{o} ) { open STDOUT, '>', $opts{o} or die "error: could not open: errno=$!, file=$opts{o}\n"; } # leading delay (good if need time to get something else running) if ( exists $opts{s} and defined $opts{s} and $opts{s} > 0 ) { sleep $opts{s}; } # force flush on output for better replay of logs $| = 1; while (<>) { chomp; # TODO will need to read different log formats (standard syslog, squid, # apache, custom...): split into "time, rest of data" and have means to # output "time with format, rest of data" my %data; ( $data{rawtime}, $data{suffix} ) = $_ =~ m/^ (\w{3} \s\s? \d\d? \s [\d:]{8}) (.*) $/x; $data{epoch} = $random_seed ? $previous_epoch + rand $random_seed : syslog_to_epoch( $data{rawtime} ); if ( defined $previous_epoch ) { my $delay = ( $data{epoch} - $previous_epoch ) / $speed_factor; # KLUGE syslog messages may have different timezones, which results # in negative time differences. Workaround: detect and sleep random # time value. (TODO use moving average instead?) if ( $delay < 0 ) { $delay = rand ($random_seed || 3); } sleep $delay; } # TODO for output, either want original string, or rework with new # timestamp based on the current time. Allow templating off %data, # different time formatting via strftime. if ($random_seed) { print epoch_to_str( $data{epoch} ), $data{suffix}, $/; } else { print $_, $/; } $previous_epoch = $data{epoch}; } sub epoch_to_str { # epoch may have .123 microseconds (squid logs), so whack that off strftime "%b %d %H:%M:%S", localtime( int $_[0] || 0 ); } # convert default syslog timestamp into epoch seconds. Uses current # year, as syslog does not record the year (nor the timezone, but that's # another rant). sub syslog_to_epoch { my $date = shift; my $epoch; my %month_map; @month_map{qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec)} = 0 .. 11; my ( $month, $day, $hour, $min, $sec ) = $date =~ m/^(\w{3}) \s\s? (\d\d?) \s (\d\d):(\d\d):(\d\d)/x; $epoch = timelocal $sec, $min, $hour, $day, $month_map{$month}, 1900 + (localtime)[5]; return $epoch; } sub print_help { print <<"HELP"; Usage: $0 [-o output-file] [-f factor] [-r random-seed] < input-file Replays logs, simulating the time delays present in the source data. Options: -h/-? Display this message. -o ff Write output to this file instead of standard out. -f nn Numeric factor to speed replay up by, such as 3600. -r nn Specify random seed to rand() for random replay speed. -s nn Sleep this long before starting. Run perldoc(1) on this script for additional documentation. HELP exit 100; } __DATA__ =head1 NAME replay-log - replays logs with simulated time delays =head1 SYNOPSIS Replay logs from C (may take some time!): $ replay-log logs to a named pipe, read them with C: $ mkfifo logfile $ grep sshd or SEC: http://swatch.sourceforge.net/ http://kodu.neti.ee/~risto/sec/ Only supports Unix syslogd(8) data at present. =head2 Normal Usage $ replay-log [-o output-file] [-f factor] [-r random-seed] < input-file See L<"OPTIONS"> for details on the command line switches supported. Log data is read from standard input, and sent to standard output, unless a custom output file is specified. =head1 OPTIONS This script currently supports the following command line switches: =over 4 =item B<-h>, B<-?> Prints a brief usage note about the script. =item B<-o> I Send output to I instead of standard out. =item B<-r> I Use I as the random seed to C; logs will playback using a random delay based on the seed. =item B<-f> I Speed playback of logs. I must be a number greater than one; higher values lead to faster replays. =item B<-s> I Sleep I seconds before replaying any logs. =back =head1 BUGS =head2 Reporting Bugs If the bug is in the latest version, send a report to the author. Patches that fix problems or add new features are welcome. =head2 Known Issues Only supports Unix syslogd(8) data at present. Output could be formatted in different ways and offer better control of timestamp display. =head1 SEE ALSO perl(1) =head1 AUTHOR Jeremy Mates, http://sial.org/contact/ =head1 COPYRIGHT The author disclaims all copyrights and releases this script into the public domain. =head1 VERSION $Id: replay-log,v 1.9 2005/08/06 21:00:00 jmates Exp $ =cut