Agent-TCLI
view release on metacpan or search on metacpan
lib/Agent/TCLI/Package/Tail.pm view on Meta::CPAN
:Arg('name'=>'tests_complete','default'=>0)
:Acc('tests_complete');
=back
=head2 METHODS
Most of these methods are for internal use within the TCLI system and may
be of interest only to developers trying to enhance this module.
=over
=item Append <input>, <wheel_id>
This POE Event handler receives the tail events and creates the
line objects to insert into the line_cache. It typically
accepts events from POE::Wheel::FollowTail. It may also be
called directly from another POE Session, in which case only
the input to be logged should be provided. It will insert the
sending POE Session as the line->source if no wheel_id is provided.
=cut
sub Append {
my ($kernel, $self, $sender, $input, $wheel_id) =
@_[KERNEL, OBJECT, SENDER, ARG0, ARG1];
# This and Log are virtually identical. Maybe merge someday?
return unless defined $input;
# assign source to either a wheel or another POE session
my $source = defined($wheel_id)
? $self->GetWheelKey($wheel_id, 'source')
: $sender ;
$self->Verbose("append: input(".$input.") from ".$source, 2 );
$line_count[$$self]++;
my $type = ref($input);
$type = "line" if ($type eq '');
# push line onto cache
$self->push_line_cache( Agent::TCLI::Package::Tail::Line->new(
'input' => $input,
'count' => $line_count[$$self],
'birth_time' => time(),
'ttl' => time()+ $self->line_hold_time,
'source' => $source,
'type' => $type,
));
# remove first-in line if total line count exceeded.
if ( $self->depth_line_cache > $self->line_max_cache )
{
$self->Verbose('Too many lines, removing...');
shift ( @{$self->line_cache} );
}
# post new event to active states
foreach my $state ( sort keys %{$self->active} )
{
$kernel->yield( $state => 'Append', $self->line_count );
}
}
=item Activate
This POE event handler activates tests in the queue by registering an
event with SimpleLog and creating an event handler.
This whole process is currently ineffecient and will hopefully get
redone sometime.
=cut
sub Activate {
my ($kernel, $self, ) =
@_[KERNEL, OBJECT, ];
my $counter = $self->activated_count;
$self->Verbose('Activate: counter('.$counter.') ',2);
# remember that counter is an array index and is one less than the size...
if ($self->depth_test_queue == 0 || $counter >= $self->depth_test_queue )
{
$kernel->delay('Activate',$self->interval );
return('nothing activated');
}
my $test = $self->test_queue->[$counter];
$self->Verbose('Activate: counter('.$counter.') dump ',4,$self->test_queue);
$self->Verbose('Activate: test_num('.$test->num.') dump '.$test->dump(1),4);
$self->increment_activated_count;
$kernel->delay('Activate',$self->interval );
my $num = $test->num;
#put into active list
$self->active->{$num} = $test;
# set up test TTL. We add the time so that we now know the exact
# expiration of this event.
# Note that a sufficiently large number for TTL could get a test to last
# for years...
$test->increment_ttl( time() ) if ( $test->ttl != 0 );
$self->Verbose('Activate: counter('.$counter.') time('.time().') test dump ',3,$test );
# Set up state to receive an event for this test
$kernel->state( $num => $self => $test->handler );
$self->Verbose('Activate: state('.$num.') handler('.$test->handler.')',1);
# kick off state to process cache
$kernel->yield( $num );
return('activated '.$num );
}
=item Check
The POE event handler is what does the actual test/watch on the line
objects.
=cut
sub Check {
my ($kernel, $self, $sender, $session, $state ) =
@_[KERNEL, OBJECT, SENDER, SESSION, STATE ];
# Note the right now we're ignoring the ARGS which has the
# Line number, since we keep track of that in each test.
# The line number is not supplied if we're processing the cache
# This might be used for optimization in the future
$self->Verbose('Check: state('.$state.') lines('.$self->depth_line_cache.
') completed('.$self->tests_complete.') ',1);
$self->Verbose('Check: state('.$state.') test queue dump ',5,$self->test_queue );
# OK, so I actually had a bug where I created a Check event with no event name.
return unless defined ($state);
my $test = $self->test_queue->[$state -1];
$self->Verbose('Check: state('.$state.') test dump ',4,$test );
# Catch any events posted after this test completed
return if ( $test->complete );
# if ordered, make sure previous test has completed
# BUG This only works if all previous tests are ordered.
# Though it mostly does it right.
$self->Verbose('Check: ordered('.$test->ordered.') state('.$state.
') previous complete('.$self->test_queue->[$state - 2]->complete.
') complete('.$self->tests_complete.')',1);
if ( $test->ordered && ( $state > 1 ) &&
!$self->test_queue->[$state - 2]->complete
)
{
$self->Verbose('Check: state('.$state.') ordered is on, previous not complete.');
return;
}
# Get time here so that all checking uses same time
my $time = time();
my ($because, $comment, $code, $input, $matchline);
my $ok;
#loop over line of input (in order)
my $line_index = 0;
LINE: while ( $line_index < @{$line_cache[$$self]} )
{
my $line = $self->line_cache->[$line_index];
$ok = 0;
$self->Verbose('Check: state('.$state.') LINE dump ',4,$line );
# if line.index_counter > test.index_counter
# this is a line we haven't checked out
$self->Verbose('Check: num('.$test->num.') $line->count('.$line->count.
') $test->last_line('.$test->last_line.')', 1 - $test->verbose );
if ($line->count > $test->last_line )
{
$input = $line->input;
# get test
$code = $test->code;
$self->Verbose('Check: state('.$state.') input('.$input.
') $code->($input) = ('.$code->( $input ).')',2);
$self->Verbose('Check: num('.$test->num.') input('.$input.
') $code->($input) = ('.$code->( $input ).')',0)
if ($test->verbose);
# remove line if match, increment count
if ( $code->( $input ) )
{
$ok = 1;
$test->increment_match_count;
# TODO insert optional line pruning...
$matchline = splice( @{$self->line_cache}, $line_index, 1 );
$self->Verbose("Check: lc(".$line->count.") ok($ok".
") li($line_index) matchline ".$matchline->dump(1),2 )
if ($test->verbose);
}
# $self->Verbose('Check: in loop('.$line->count.') ( test dump ',2,$test );
# set test.index_counter to line's
$test->last_line ($line->count);
$test->increment_line_count;
# report line if feedback and match or verbose
if ($test->feedback && ( $ok || $test->verbose) )
{
$test->request->Respond( $kernel, $input, 200);
}
$self->Verbose('Check: num('.$test->num.') line_count('.
$test->line_count.') max_lines('.$test->max_lines.
") passed(".$test->match_count.") last_line{".
$test->last_line.')',0 )
if ($test->verbose);
# check if we passed enough times
if ( $ok && $test->match_count == $test->match_times )
{
$self->Verbose('Check: passed, skipping rest of lines',2);
$test->success(1);
last LINE;
}
#check lines_seen and indicate failed test if necessary
if ( ( $test->max_lines != 0 &&
$test->line_count >= $test->max_lines) )
{
$self->Verbose('Check: fail state('.$state.') TEST dump ',2,$test );
$because .= "Seen too many lines. Saw (".$test->line_count.") max(".
$test->max_lines.") passed(".$test->match_count.") \n".
"Last line: ".$input;
last LINE;
}
}
# if it passed, we took out the line, so don't increment.
$line_index++ unless($ok);
}
# $self->Verbose('Check: post loop state('.$state.') test dump ',2,$test );
# $self->Verbose('Check: post loop $ok('.$ok.') matchline',2,$matchline);
if ( ($test->match_times != 0) && ($test->match_count == $test->match_times) )
{
$kernel->call($self->name => 'Complete' => $state => 'ok' );
$test->complete(1);
}
# check clock and fail test if necessary
elsif ( ( $test->ttl != 0 ) && ( $time > $test->ttl ) )
{
$because .= "Timer expired. Time(".$time.") TTL(".$test->ttl.
") Diff(".($test->ttl - $time).")";
}
if ( ($test->match_times != 0) && $because && not $test->success )
{
$self->Verbose("Check: failing ok($ok) because'$because'");
$kernel->call($self->name => 'Complete' => $state => 'not ok' => $because );
$test->complete(1);
}
# if we're done, clean up
if ( $test->complete )
{
# remove the test from active list
delete($self->active->{$test->num});
# remove the session state
$self->Verbose('Complete: removing: state('.$test->num.')',1);
$kernel->state( $state );
}
}
=item Complete
This POE event handler handles the response when a test/watch
is complete.
=cut
sub Complete {
my ($kernel, $self, $session, $state, $result, $because ) =
@_[KERNEL, OBJECT, SESSION, ARG0, ARG1, ARG2 ];
$self->Verbose("Complete: state(".$state.") result(".$result.
") ");
my $test = $self->test_queue->[$state -1];
$self->Verbose('Check: state('.$state.') test dump ',4,$test );
my $request = $test->request;
my ($txt, $code);
if ( $result eq 'ok' )
{
$test->success(1);
$txt = 'ok '.$test->name;
$code = 200;
}
elsif ( $result eq 'not ok')
{
# TODO. Need a better way of returning the because for diagnostics?
( run in 3.442 seconds using v1.01-cache-2.11-cpan-5735350b133 )