Greetings Monks,

Do you have any experience using FSA::Rules module? I'm trying to use a Finite State Machine to parse a log file and thus the usage of FSA::Rules.

The module has some difficulties to use (the classes interfaces are a bit weird to use and the documentation lacks a good example of usage) but my problem is related to the state change.

The parser initiates in a state that should try to "guess" which is the content being read. Since the log files (in normal conditions) should start with a "welcome message", the state machine goes from "reading the first line" to the "greetings" state and keep reading the lines (in the greetings state) until a command prompt is found. After that, the "greetings" state should changed to "command_submission" state, where the prompt and command will be parsed so it's possible to identify what kind of output will be found in the next line.

With this logic in mind, the "greetings" state should goes to "command_submission" and never execute again. Until that happens, the next lines should be processed by it.

The problem is, I'm not being able to apply this logic to FSA::Rules: I've being playing around with FSA::Rules::rules and the strict parameter during object creation, but the switch method always finishes with the message:

Cannot determine transition from state "greetings" at C:\temp\monitor/Siebel/Srvrmgr/ListParser.pm line 297

Looking at the code details of the module I saw that if the switch method does not find any rules method that returns true it will raise the exception. The thing is, "greetings" state has only one possibility to go to another state ("command_submission") and should keep processing until this state is reached.

I "solved" this putting a "default" state (always returns true) inside rules going to the greetings state itself. It keeps calling itself until the prompt is found. I don't think so this is the correct way to do that and, besides, I would like to use on_exit to produce the output of the greetings read and every time "greetings" goes to rules, on_exit is executed.

It's a bit complicated to explain. I hope the code below to helps to understand better the problem.

package Siebel::Srvrmgr::ListParser; use Moose; use FSA::Rules; use lib 'c:/temp/monitor'; use Siebel::Srvrmgr::ListParser::Output; use Data::Dumper; has 'parsed_tree' => ( is => 'rw', isa => 'ArrayRef', reader => 'get_parsed_tree', writer => '_set_parsed_tree' ); #has 'default_prompt' => # ( is => 'rw', isa => 'Str', reader => 'get_prompt', writer => 'set_ +prompt' ); has 'has_tree' => ( is => 'rw', isa => 'Bool', default => 0 ); has '_list_comp_format' => ( is => 'ro', isa => 'Str', reader => 'get_list_comp_format' ); has 'prompt_regex' => ( is => 'rw', isa => 'RegexpRef', reader => 'get_prompt_regex', writer => 'set_prompt_regex', default => sub { qr/^srvrmgr(\:\w+)?>(\s[\w\s]+)?/ } ); has 'hello_regex' => ( is => 'rw', isa => 'RegexpRef', reader => 'get_hello_regex', writer => 'set_hello_regex', default => sub { qr/^Siebel\sEnterprise\sApplications\sSiebel\sServer\sManager\,\sVersi +on\s\d+\.\d+\.\d+\s\[\d+\]\sLANG_INDEPENDENT\s?/; } ); has '_buffer' => ( is => 'rw', isa => 'ArrayRef', reader => 'get_buffer', writer => '_set_buffer', default => sub { return [] } ); has 'command' => ( is => 'rw', isa => 'Str', reader => 'get_command', writer => 'set_command', default => '' ); sub set_buffer { my $self = shift; my $value = shift; if ( defined($value) ) { my $buffer_ref = $self->get_buffer(); push( @{$buffer_ref}, $value ); $self->_set_buffer($buffer_ref); } } sub clean_buffer { my $self = shift; $self->_set_buffer( [] ); } sub count_parsed { my $self = shift; return scalar( @{ $self->get_parsed_tree() } ); } sub clean_parsed_tree { my $self = shift; $self->has_tree(0); } sub set_parsed_tree { my $self = shift; my $output = shift; if ( $self->has_tree() ) { my $old_parsed_tree = $self->get_parsed_tree(); push( @{$old_parsed_tree}, $output ); $self->_set_parsed_tree($old_parsed_tree); } else { $self->_set_parsed_tree( [$output] ); } $self->has_tree(1); } sub append_output { my $self = shift; my $data_type = shift; my $data_ref = shift; my $output = Siebel::Srvrmgr::ListParser::Output->new( { data_type => $data_type, data_parsed => $data_ref } ); $self->set_parsed_tree($output); } sub parse { my $self = shift; # array ref my $data_ref = shift; die "data parameter must be an array reference\n" unless ( ref($data_ref) eq 'ARRAY' ); my $fsa = FSA::Rules->new( # { strict => 1 }, #params first_line => { do => sub { print "Starting reading the data\n" }, rules => [ command_submission => sub { my $state = shift; my $line = $state->notes('line'); $line =~ s/\r\n//g; return $line =~ /$state->notes('parser')->get_prompt_regex()/; }, greetings => sub { my $state = shift; my $line = $state->notes('line'); $line =~ s/\r\n//g; return $line =~ $state->notes('parser')->get_hello +_regex(); } ], message => 'First line read' }, greetings => { do => sub { my $state = shift; if ( defined( $state->notes('line') ) ) { print "hello\n"; $state->notes('parser') ->set_buffer( $state->notes('line') ); } }, on_exit => sub { my $state = shift; print Dumper( $state->notes('parser')->get_buffer() ); }, #on_exit => sub { print Dumper( $state->notes('parser')->parse_greeti +ngs() ) }, rules => [ command_submission => sub { my $state = shift; return ( $state->notes('line') =~ $state->notes('parser')->get_prompt_regex() +); }, greetings => sub { return 1; } # :TRICKY:05-07-2011:arfreitas: should check why i +s necessary to do this ], message => 'prompt found' }, list_comp => { do => sub { my $state = shift; $state->notes('parser')->set_buffer( $state->notes('li +ne') ); }, rules => [ command_submission => sub { my $state = shift; return $state->notes('line') =~ /$state->notes('parser')->get_prompt_regex()/; } ], on_exit => sub { $self->parse_list_comp() }, message => 'prompt found' }, command_submission => { do => sub { my $state = shift; print "got it\n"; print $state->notes('line'), "\n"; my $cmd = ( $state->notes('line') =~ $state->notes('parser')->get_prompt_regex() )[1] +; $state->notes('parser')->set_command($cmd); }, rules => [ list_comp => sub { my $state = shift; if ( $state->notes('parser')->get_command() eq 'li +st_comp' ) { return 1; } else { return 0; } } ], message => 'command submitted' } ); my $state; foreach my $line ( @{$data_ref} ) { chomp($line); unless ( defined($state) ) { $state = $fsa->start(); $state->notes( parser => $self ); } $state->notes( line => $line ); $fsa->switch(); } $fsa->done(); } # nothing seems to be interesting to do with the greetings text sub parse_greetings { my $self = shift; my @own_data; foreach my $line ( @{ $self->get_buffer() } ) { chomp($line); next if $line eq ''; push( @own_data, $line ); } $self->clean_buffer(); return \@own_data; } sub parse_list_comp { my $self = shift; my $data_ref = $self->get_buffer(); my @parsed_lines; # removing the command itself shift( @{$data_ref} ); my $header_line = shift( @{$data_ref} ); my @columns = split( /\s{2,}/, $header_line ); # removing the lines after the columns header shift( @{$data_ref} ); # removing the three last lines Siebel 7.5.3 for ( 1 .. 3 ) { pop( @{$data_ref} ); } foreach my $line ( @{$data_ref} ) { chomp($line); next if $line eq ''; my @fields_values = split( /\s{2,}/, $line ); my %line; my $list_len = scalar(@columns); for ( my $i = 0 ; $i < $list_len ; $i++ ) { $line{ $columns[$i] } = $fields_values[$i]; } push( @parsed_lines, \%line ); } $self->clean_buffer(); return \@parsed_lines; } no Moose; __PACKAGE__->meta->make_immutable;
package Siebel::Srvrmgr::ListParser::Output; use Moose; has 'data_type' => ( is => 'rw', isa => 'Str' ); has 'data_parsed' => ( is => 'rw', isa => 'ArrayRef' ); no Moose; __PACKAGE__->meta->make_immutable;

Thanks for the help,

Alceu Rodrigues de Freitas Junior
---------------------------------
"You have enemies? Good. That means you've stood up for something, sometime in your life." - Sir Winston Churchill

In reply to Usage of FSA::Rules to parse log files by glasswalk3r

Title:
Use:  <p> text here (a paragraph) </p>
and:  <code> code here </code>
to format your post, it's "PerlMonks-approved HTML":



  • Posts are HTML formatted. Put <p> </p> tags around your paragraphs. Put <code> </code> tags around your code and data!
  • Titles consisting of a single word are discouraged, and in most cases are disallowed outright.
  • Read Where should I post X? if you're not absolutely sure you're posting in the right place.
  • Please read these before you post! —
  • Posts may use any of the Perl Monks Approved HTML tags:
    a, abbr, b, big, blockquote, br, caption, center, col, colgroup, dd, del, details, div, dl, dt, em, font, h1, h2, h3, h4, h5, h6, hr, i, ins, li, ol, p, pre, readmore, small, span, spoiler, strike, strong, sub, summary, sup, table, tbody, td, tfoot, th, thead, tr, tt, u, ul, wbr
  • You may need to use entities for some characters, as follows. (Exception: Within code tags, you can put the characters literally.)
            For:     Use:
    & &amp;
    < &lt;
    > &gt;
    [ &#91;
    ] &#93;
  • Link using PerlMonks shortcuts! What shortcuts can I use for linking?
  • See Writeup Formatting Tips and other pages linked from there for more info.