package Logfile::EPrints::Filter::Session; use strict; use vars qw( %SESSIONS $AUTOLOAD $TIDY_ON $TIDY_COUNT ); $TIDY_ON = 10000; $TIDY_COUNT = 0; sub new { my ($class,%self) = @_; $self{session} ||= 'Logfile::EPrints::Session'; $TIDY_COUNT = 0; bless \%self, ref($class) || $class; } sub AUTOLOAD { return if $AUTOLOAD =~ /[A-Z]$/; $AUTOLOAD =~ s/^.*:://; my( $self, $hit ) = @_; my $address = $hit->address; if( exists $SESSIONS{$address} and $SESSIONS{$address}->expired_by( $hit ) ) { delete($SESSIONS{$address})->end_session; } my $session = $SESSIONS{$address} ||= $self->{session}->new( filter => $self, address => $address, ); $session->$AUTOLOAD( $hit ); $self->_tidyup( $hit ) if ++$TIDY_COUNT > $TIDY_ON; $hit->{session} = $session; return $self->{handler}->$AUTOLOAD($hit); } sub _tidyup { my( $self, $hit ) = @_; $TIDY_COUNT = 0; for(keys %SESSIONS) { if( $SESSIONS{$_}->expired_by( $hit ) ) { delete($SESSIONS{$_})->end_session; }; } } package Logfile::EPrints::Session; =head1 NAME Logfile::EPrints::Session - Simple session class =head1 METHODS =over 4 =cut use strict; use warnings; use vars qw( $AUTOLOAD $MAX_SESSION_GAP ); $MAX_SESSION_GAP = 60*10; # 10 minutes sub new { my( $class, %self ) = @_; bless \%self, $class; } =item $session->expired_by( $hit ) Returns true if this session would be expired before $hit occurred. NOTE for the purposes of tidyup $hit may not be from the same address as the session. =cut sub expired_by { ($_[1]->utime - $_[0]->{last_seen}) > $MAX_SESSION_GAP } =item $session->start_session( $hit ) A new session has started with $hit. =cut sub start_session {} =item $session->end_session The session has expired/finished. =cut sub end_session { delete $_[0]->{last_abstract} } =item $session->total( [ $type ] ) Return the total number of requests in this session or, if $type is given, total unique requests (by identifier) for $type. =cut sub total { my( $self, $type ) = @_; return @_ == 2 ? scalar keys %{$self->{requests}->{$type}} : $self->{requests}->{total}; } sub AUTOLOAD { return if $AUTOLOAD =~ /[A-Z]$/; $AUTOLOAD =~ s/^.*:://; my( $self, $hit ) = @_; if( !defined $self->{last_seen} ) { $self->{first_seen} = $hit->utime; $self->start_session( $hit ); } if( $AUTOLOAD eq 'abstract' ) { $self->{last_abstract} = $hit; # creates a loop in this hit } elsif( $AUTOLOAD eq 'fulltext' and exists $self->{last_abstract} ) { if( $self->{last_abstract}->identifier eq $hit->identifier ) { $hit->{abstract_referrer} = $self->{last_abstract}; } else { delete $self->{last_abstract}; } } $self->{last_seen} = $hit->utime; if( $AUTOLOAD eq 'abstract' or $AUTOLOAD eq 'fulltext' ) { $self->{requests}->{total}++; $self->{requests}->{$AUTOLOAD}->{$hit->identifier}++; } } package Logfile::EPrints::Filter::MaxPerSession; use strict; use warnings; our @ISA = qw( Logfile::EPrints::Filter ); use vars qw( $AUTOLOAD ); sub AUTOLOAD { return if $AUTOLOAD =~ /[A-Z]$/; $AUTOLOAD =~ s/^.*:://; my( $self, $hit ) = @_; if( defined($self->{$AUTOLOAD}) and $hit->{session}->total($AUTOLOAD) > $self->{$AUTOLOAD} ) { return undef if $hit->{session}->{__PACKAGE__ . '_removed'}; $hit->{session}->{__PACKAGE__ . '_removed'} = 1; return Logfile::EPrints::Hit::Negate->new( address => $hit->address, start_utime => $hit->{session}->{first_seen}, end_utime => $hit->{session}->{last_seen}, ); } else { return $self->{handler}->$AUTOLOAD( $hit ); } } 1; __END__ =back