package Easy::Log::Filter; our $this_package; our ($FILTER_REGEX, $NOT_FILTER_REGEX, $FILTER_ALL_REGEX, $MATCH_LOG_LEVEL_REGEX, $FILTER, $NOT_FILTER, $FILTER_CNT ); # if any $(.*)log->write(...)'s are in the calling code, and the log level is specified with one of the followin prepended with a '$' use constant DEFAULT_FILTER => (qw( mll lll cll qll ell all wll nll ill dll tll sll ), map { "dl$_" } ( 0 .. 9, 99 )); use constant LOG_LEVELS => ( map { $_ => "D_$_"; } qw( MESSAGE LOUD CLEAN QUIT EXIT EMERG ALERT CRIT FATAL FAIL ERROR WARN NOTICE INFO DEBUG TRACE SPEW ), map { "DEBUG$_"} ( 0 .. 9, 99 ) ); BEGIN { sub space { my $sp = 'space'; # this is a stupid little subroutine to nicely display stuff # could probably use a format specifier or sprintf better, but what the hell # I mostly use this for aligning output nicely my $piece = shift; my $max = shift; defined $max or $max = 27; my $separator = shift; unless( defined $separator and length $separator > 0 ) { $separator = ' '; } my $lp = defined $piece ? length $piece : 0; my $ls = length $separator; my $multiplier = $lp < $max ? int (( $max - $lp )/$ls ) : 0; my $spacer = $separator x $multiplier; my $return = $sp eq 'space' ? $piece . $spacer : $spacer . $piece; my $lr = length $return; my $finisher = $lr < $max ? ( ' ' x ( $max - $lr )) : ''; $return = $sp eq 'space' ? ( $return . $finisher ) : ( $finisher . $return ); return $return; } sub pad { my $sp = 'pad'; # this is a stupid little subroutine to nicely display stuff # could probably use a format specifier or sprintf better, but what the hell # I mostly use this for aligning output nicely my $piece = shift; my $max = shift; defined $max or $max = 27; my $separator = shift; unless( defined $separator and length $separator > 0 ) { $separator = ' '; } my $lp = defined $piece ? length $piece : 0; my $ls = length $separator; my $multiplier = $lp < $max ? int (( $max - $lp )/$ls ) : 0; my $spacer = $separator x $multiplier; my $return = $sp eq 'space' ? $piece . $spacer : $spacer . $piece; my $lr = length $return; my $finisher = $lr < $max ? ( ' ' x ( $max - $lr )) : ''; $return = $sp eq 'space' ? ( $return . $finisher ) : ( $finisher . $return ); return $return; } sub space_pad { my $sp = pop; # this is a stupid little subroutine to nicely display stuff # could probably use a format specifier or sprintf better, but what the hell # I mostly use this for aligning output nicely my $piece = shift; my $max = shift; defined $max or $max = 27; my $separator = shift; unless( defined $separator and length $separator > 0 ) { $separator = ' '; } my $lp = defined $piece ? length $piece : 0; my $ls = length $separator; my $multiplier = $lp < $max ? int (( $max - $lp )/$ls ) : 0; my $spacer = $separator x $multiplier; my $return = $sp eq 'space' ? $piece . $spacer : $spacer . $piece; my $lr = length $return; my $finisher = $lr < $max ? ( ' ' x ( $max - $lr )) : ''; $return = $sp eq 'space' ? ( $return . $finisher ) : ( $finisher . $return ); return $return; } # sub space { # space_pad( @_, 'space'); # } # sub pad { # space_pad( @_, 'pad'); # } #die; $this_package = __PACKAGE__; $ENV{LOG_PACKAGES_DEBUG} ||= 0; $ENV{LOG_FILTER_DEBUG} = exists $ENV{LOG_FILTER_DEBUG} ? $ENV{LOG_FILTER_DEBUG} : 0; $ENV{LOG_FILTER_PACKAGES_DEBUG} = exists $ENV{LOG_FILTER_PACKAGES_DEBUG} ? $ENV{LOG_FILTER_PACKAGES_DEBUG} : 0; # want to make it so you can see what the filter is doing for specified packages only $ENV{LOG_FILTER} ||= 'ON'; $ENV{LOG_INTERNAL_DEBUG} ||= 0; print STDERR "THIS_PACKAGE=$this_package\n" if $ENV{LOG_FILTER_DEBUG}; my @DEFAULT_FILTER= DEFAULT_FILTER(); unless ( defined $FILTER_REGEX ) { my $FILTER; if( $ENV{LOG_FILTER} =~ /^off$/i ) { print STDERR __PACKAGE__, ":", __LINE__, ": ", "FILTER: IS OFF\n" if $ENV{LOG_FILTER_DEBUG}; $FILTER = []; $NOT_FILTER = [ @DEFAULT_FILTER ]; } elsif( $ENV{LOG_FILTER} =~ /^(on|\d+)$/i ) { print STDERR __PACKAGE__, ":", __LINE__, ": ", "FILTER: IS ON\n" if $ENV{LOG_FILTER_DEBUG}; $FILTER = [ @DEFAULT_FILTER ]; $NOT_FILTER = []; } else { print STDERR __PACKAGE__, ":", __LINE__, ": ", "FILTER: IS SPECIAL FILTER=$ENV{LOG_FILTER}\n" if $ENV{LOG_FILTER_DEBUG}; my %not_filter = (); my %filter = (); foreach my $piece ( split( /:/, $ENV{LOG_FILTER} )) { if ( $piece =~ /^\!/ ) { $piece =~ s/^\!//; $not_filter{$piece} = $piece; } else { $filter{$piece} = $piece; } } print STDERR __PACKAGE__, ":", __LINE__, ": ", "\%filter: ", scalar keys %filter , ":", join( ', ', keys %filter ), "\n" if $ENV{LOG_FILTER_DEBUG}; print STDERR __PACKAGE__, ":", __LINE__, ": ", "\%not_filter: ", scalar keys %not_filter, ":", join( ', ', keys %not_filter ), "\n" if $ENV{LOG_FILTER_DEBUG}; if ( scalar keys %filter ) { $FILTER = [ keys %filter ]; $NOT_FILTER = []; } else { # $FILTER = [ map { ($not_filter{$_} and $_ =~ /$not_filter{$_}/ )? () : $_; } ( @DEFAULT_FILTER ) ]; # $NOT_FILTER = [ map { ($not_filter{$_} and $_ =~ /$not_filter{$_}/ )? $_ : (); } ( @DEFAULT_FILTER ) ]; my $not_filter_rx = join('|', values %not_filter); $not_filter_rx = qr/$not_filter_rx/; print STDERR __PACKAGE__, ":", __LINE__, ": ", "not_filter_rx : '$not_filter_rx'\n" if $ENV{LOG_FILTER_DEBUG}; $FILTER = [ map { $_ =~ m/$not_filter_rx/ ? () : $_; } ( @DEFAULT_FILTER ) ]; $NOT_FILTER = [ map { $_ =~ m/$not_filter_rx/ ? $_ : (); } ( @DEFAULT_FILTER ) ]; } } print STDERR __PACKAGE__, ":", __LINE__, ": ", "FILTER: ", join('|', @$FILTER ), "\n" if $ENV{LOG_FILTER_DEBUG}; #$FILTER_REGEX = '\$[_a-zA-Z]*[_a-zA-Z0-9]*log->write\(.*?\$(' . join('|', @$FILTER) . '),.*?\);'; $FILTER_REGEX = '\$[_a-zA-Z]+[_a-zA-Z0-9]*->write\(.*?\$(' . join('|', @$FILTER) . '),.*?\);'; print STDERR __PACKAGE__, ":", __LINE__, ": ", "FILTER_REGEX : $FILTER_REGEX\n" if $ENV{LOG_FILTER_DEBUG}; print STDERR __PACKAGE__, ":", __LINE__, ": ", "NOT_FILTER: ", join('|', @$NOT_FILTER ), "\n" if $ENV{LOG_FILTER_DEBUG}; #$NOT_FILTER_REGEX = '\$[_a-zA-Z]*[_a-zA-Z0-9]*log->write\(.*?\$(' . join('|', @$NOT_FILTER) . ')(\,|\ \,|\,\ ).*?\);'; $NOT_FILTER_REGEX = '\$[_a-zA-Z]+[_a-zA-Z0-9]*->write\(.*?\$(' . join('|', @$NOT_FILTER) . ')(\,|\ \,|\,\ ).*?\);'; print STDERR __PACKAGE__, ":", __LINE__, ": ", "NOT_FILTER_REGEX : $NOT_FILTER_REGEX\n" if $ENV{LOG_FILTER_DEBUG}; } unless ( defined $FILTER_ALL_REGEX ) { my $FILTER = [ @DEFAULT_FILTER ]; # $FILTER_ALL_REGEX = '(\s+\$[_a-zA-Z]*[_a-zA-Z0-9]*log->write\(.*?)(' . join('|', @$FILTER) . ')(\,|\ \,|\,\ )(.*?\);)'; $FILTER_ALL_REGEX = '(\$[_a-zA-Z]*[_a-zA-Z0-9]*log->write\(.*?)(' . join('|', @$FILTER) . ')(\,|\ \,|\,\ )(.*?\);)'; print STDERR __PACKAGE__, ":", __LINE__, ": ", "FILTER_ALL_REGEX: $FILTER_ALL_REGEX\n" if $ENV{LOG_FILTER_DEBUG}; } unless ( defined $MATCH_LOG_LEVEL_REGEX ) { my $FILTER = [ LOG_LEVELS() ]; $MATCH_LOG_LEVEL_REGEX = '(\$[_a-zA-Z]*[_a-zA-Z0-9]*log->write\(.*?)(' . join('|', @$FILTER) . ')(\,|\ \,|\,\ )(.*?\);)'; print STDERR __PACKAGE__, ":", __LINE__, ": ", "MATCH_LOG_LEVEL_REGEX: $MATCH_LOG_LEVEL_REGEX\n" if $ENV{LOG_FILTER_DEBUG}; } } use Filter::Simple; our $replace = '1;'; FILTER { # this filters out unwanted log messages from source code BEFORE COMPILATION # proves to be a great boon to performance $FILTER_CNT++; ##print STDERR __LINE__, ": \$ENV{LOG_FILTER} = $ENV{LOG_FILTER}\n"; return if ( $ENV{LOG_FILTER} and $ENV{LOG_FILTER} =~ /^(OFF|)$/i); #return if ( $before =~ /\s*/s ); my @caller = caller(1); $ENV{LOG_FILTER_DEBUG} ||= 0; print STDERR __PACKAGE__, ":", __LINE__, ": ", "CALLER: \n\t", join("\t\n", map { (defined $_ ? $_ : '')} @caller ), "\n" if ($ENV{LOG_FILTER_DEBUG} > 6); my $package = $caller[0]; my $file = $caller[1]; my $calline = $caller[2]; #print STDERR "." if $ENV{LOG_FILTER_DEBUG}; my $debug_this_package = $ENV{LOG_FILTER_PACKAGES_DEBUG} ? $file =~ /$ENV{LOG_FILTER_PACKAGES_DEBUG}/ : 1; print STDERR __PACKAGE__, ":", __LINE__, ": ", "DEBUG_THIS_PACKAGE=$debug_this_package ... CALLED FROM FILE: '$file' ($ENV{LOG_FILTER_PACKAGES_DEBUG})\n" if $ENV{LOG_FILTER_DEBUG}; my $not_filtered = $ENV{LOG_FILTER_DEBUG} ? "' ### LOG MESSAGE UN-FILTERABLE ### '" : ''; my @match; my @before = split("\n", $_ ); my @after = (); my $linenum = $calline; my $totallines = scalar @before; my $filtered = ''; my $filtered_status = ''; foreach my $line ( @before ) { $linenum++; $filtered = ''; $filtered_status = ''; if ( $line =~ /$MATCH_LOG_LEVEL_REGEX/ ) { $filtered_status = 'UNTOUCHED'; } elsif ( $line =~ s/($FILTER_REGEX)\s*$/$replace/g ) { $filtered_status = 'FILTERED '; $filtered = $1; } elsif ( $line =~ /$NOT_FILTER_REGEX/g ) { $filtered_status = 'NOT-FILTERED'; } elsif ( $line =~ /$FILTER_ALL_REGEX/ ) { #and $line !~ /$not_filtered/ ) { print STDERR __PACKAGE__, ":", __LINE__, ": ", "WARNING DEBUG LOG MESSAGE NOT REMOVED: $file : $linenum: $line \n" if ($ENV{WARN_FILTER} or ( $debug_this_package and $ENV{LOG_FILTER_DEBUG})); $line =~ s/$FILTER_ALL_REGEX/${1}${2},${not_filtered}${3}${4}/; $filtered_status = 'CHANGED'; } push @after, $line; print STDERR __PACKAGE__, ":", __LINE__, ": ", pad(++$ENV{GLOBAL_LINES_FILTER_EXAMINED},5), ' |', pad($linenum,5), '/', space($totallines,5),': ', space($filtered_status, undef, '.'), '| ', $line, "\n" if ($debug_this_package and $ENV{LOG_FILTER_DEBUG} > 3); print STDERR __PACKAGE__, ":", __LINE__, ": ", pad('',5, 'x'), 'xx', pad('',5,'x'), 'x', space('',5,'x'),'::::: ', "FORMER CONTENTS: $filtered", "\n" if ( $debug_this_package and $ENV{LOG_FILTER_DEBUG} > 3 and $filtered ); } $_ = join( "\n", @after) . "\n"; }; 1;