#!/usr/bin/perl -w
# usage information found at the bottom or by using -h
use strict;
use Term::ReadKey;
use Time::Local;
use Cwd qw(abs_path);
$|++;
my (%Options, $wchar, $hchar);
sub get_options;
sub printNice($);
sub mon2num($);
sub timelocal_str;
sub bufferstr;
main: {
%Options = get_options();
my $ph;
if( $Options{watch_file} && $Options{watch_file} ne '-' ) {
eval { open LOG, qq{tail -n0 -f "$Options{watch_file}"|} } or die "Couldn't open pipe to tail: $!";
local $SIG{PIPE} = sub { die "tail pipe broke" };
$ph = \*LOG;
}
else {
$ph = \*STDIN;
}
my $cur;
my $lastcur = 0;
while( <$ph> ) {
chomp;
if( /^\[([^\]]+)\]/ ) {
($wchar, $hchar, ) # $wpixels, $hpixels)
= GetTerminalSize();
my $lt = timelocal_str($1);
if( $lt >= $cur + $Options{group_sec}) {
$lastcur = $cur = $lt;
print bufferstr() . localtime($lt) . ":\n";
}
if( $Options{show_times} && $lt != $lastcur ) {
$lastcur = $lt;
print( (' 'x($Options{indent} - 4)) . localtime($lt) . "\n");
}
s/^\[[^\]]+\]//;
s/\s*\[error\]\s*// if( $Options{error} );
s/\s*\[client [^\]]+\]\s*// if( $Options{client} );
s/, referer: .*// if( $Options{referer} );
foreach my $str ( @{ $Options{dir_filter} } ) {
s/$str//g;
}
printNice "$_\n";
}
}
close LOG;
}
sub printNice($) {
my $str = shift;
print join('', ' 'x($Options{indent})) . '* ' . substr($str, 0, $wchar - $Options{indent} -3, '');
while($str ne '') {
print "\n" . join('', ' 'x($Options{indent}+2)) . substr($str, 0, $wchar - $Options{indent} - 2 -1, '');
}
}
sub mon2num($) {
my $month = shift;
my %h = qw(
jan 0 feb 1 mar 2 apr 3 may 4 jun 5
jul 6 aug 7 sep 8 oct 9 nov 10 dec 11
);
return $h{ lc substr($month, 0, 3) };
}
sub timelocal_str { #does the opposite of localtime, my $seconds = timelocal( localtime().'' )
my ($str) = @_;
# Sat Jan 21 01:16:50 2006
$str =~ s/\s+|\s+/ /g; $str =~ s/^\s+/ /g; $str =~ s/\s+$/ /g;
my ($wd, $mon, $mday, $hr, $min, $sec, $yr) = split /[ \:]/, $str;
return timelocal($sec, $min, $hr, $mday, mon2num $mon, $yr);
}
sub bufferstr {
my $buffer = '';
if( $Options{buffer} ) {
my $buffer_line = '';
while( length($buffer_line) + length($Options{buffer}) < $wchar ) {
$buffer_line .= $Options{buffer};
}
$buffer .= $buffer_line;
}
$buffer .="\n"x($hchar + 1);
return $buffer;
}
sub get_options {
my %options = (
'referer' => 1,
'client' => 1,
'error' => 1,
'dir_filter' => [],
'indent' => 6,
'group_sec' => 2,
'show_times' => undef,
'watch_file' => q{-},
'buffer' => q{ == - == -},
);
while( $_ = shift @ARGV ) {
my $had_unshift = 0;
if( /^-[a-z0-9]{2,}/i ) {
my $param = substr($_, 0, 2, '');
my ($p, $v) = split /=/;
unshift @ARGV, $v if( $v );
foreach my $n ( reverse split //, $p ) {
unshift @ARGV, "-$n";
}
$_ = $param;
}
elsif( /=/ ){
my ($k, $v) = split /=/, $_, 2 ;
$_ = $k;
unshift @ARGV, $v;
$had_unshift = 1;
}
if( /--help|-h/ ) {
print usage(); exit(1);
} elsif( /--referer|-r/ ) {
$options{referer} = 0;
} elsif( /--client|-c/ ) {
$options{client} = 0;
} elsif( /--error|-e/ ) {
$options{error} = 0;
} elsif( /--dir|-d/ ) {
my $d = shift(@ARGV) || '.' ;
my $rel_d;
eval { $rel_d = abs_path($d) }; #bad paths are OK.
$d = $rel_d if $rel_d;
push @{ $options{dir_filter} }, $d ;
} elsif( /--indent|-i/ ) {
$options{indent} = shift(@ARGV);
} elsif( /--group-sec|-t/ ) {
$options{group_sec} = shift(@ARGV);
} elsif( /--buffer|-B/ ) {
$options{buffer} = shift(@ARGV);
} elsif( /--show-times|-s/ ) {
$options{show_times} = 1;
} else {
$options{watch_file} = $_;
}
}
return %options;
}
sub usage {
return q{
err_watch [-rce] [-d=dir]* [-i=n] [-t=sec] [-B=str] [watched_file]
-r,--referer Leave the referer line in the log
-c,--client Leave the [client ...] info in the log
-e,--error Leave the [error] info in the log
-d=dir,--dir Filter out the string matching dir
or the current directory (filename collapsing),
this option may be given multiple times
-i=n,--indent Indent the contents of the watched file by n spaces
-t=sec,--group-sec Group errors in sec seconds icrements
-B=str,--buffer The string defining your "group buffer string"
it will print 1 row of these repating all the way across.
-s,--show-times if set, it will print a timestamp starting wherever
the logged timestamp changes, it will still
group them according to -t however
};
}
I only worked with this one machine when I developed it, I don't know if all apache logs look like this expects them to, and I didn't feel like checking, let me know if this doesn't work with your log for some reason, and post a copy of the part that sticks out.
I know, I wrote my own command line parser... I didn't like any of the GetOpts, and well, this is pretty clean, you'll have to admit... please let me know if you find a bug in parsing your command line.
Your errors, not "you're errors", don't speak Arabic in front of us.
Use Term::ANSIColor for color coding. A color shift says more than 1K. words!
perlmonks.org content © perlmonks.org and Anonymous Monk, chanio, qbxk
prlmnks.org © 2006 edmund von der burg (eccles & toad)
v 0.03