saadatsaeed has asked for the wisdom of the Perl Monks concerning the following question:

Hello, I maybe an average joe programmer but do know perl has good text processing support. Now I'd like to go through yesterdays eventlogs and recover some useful information from there (print logs in particular). The Print logs notmally are like "Document Wod.doc owned by DUSASAE was printed on HPLJ5 via port LPT1. Size in bytes: 37836; pages printed: 1" Now I'd like the pages printed, filename, username on separate fields. Here is the code I used to do it. Most of the cost is copy/paste from various helpfiles and searches on the net. In the long term I'd just write this info to an Access Db for further reporting, more on that later.
use Win32::EventLog; $handle=Win32::EventLog->new("System", $ENV{ComputerName}) or die "Can't open Application EventLog\n"; $handle->GetNumber($recs) or die "Can't get number of EventLog records\n"; $handle->GetOldest($base) or die "Can't get number of oldest EventLog record\n"; ($sec1,$min1,$hour1,$mday1,$mon1,$year1,$wday1,$yday1,$isdst1) = local +time(time()-86400); $year1 +=1900; $mon1++; print "$mday1 $mon1 $year1\n"; while ($x < $recs) { $handle->Read(EVENTLOG_FORWARDS_READ|EVENTLOG_SEEK_READ, $base+$x, $hashRef) or die "Can't read EventLog entry #$x\n"; if ($hashRef->{Source} eq "Print") { ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = lo +caltime($hashRef->{'TimeGenerated'}); $year += 1900; $mon++; #print "$mday1 $mday \n"; #if (($mday1 eq $mday)&($mon eq $mon1)&($year eq $year1)) +{ # Win32::EventLog::GetMessageText($hashRef); # print "Entry $x: $mday $mon $year $hashRef->{Message} +\n"; # $Message = $hashRef->{Message}; #} } $x++; }

Replies are listed 'Best First'.
Re: Extracting useful information from Windows Event Logs (look)
by tye (Sage) on May 24, 2007 at 13:54 UTC

    Dump the $hashRef and look at what is in it. I suspect that the individual data items are already separated out in there so you don't need to generate the text version of the event and don't need to parse the items out of that.

    - tye        

      ++tye; you are correct!
      $hashRef->{Strings} is a string containing the data items joined by NULL characters.

Re: Extracting useful information from Windows Event Logs
by NetWallah (Canon) on May 24, 2007 at 15:12 UTC
    Here are some key code fragements from an old but functional program I wrote/plagerized. (/msg me for complete code). This should assist you in getting relevant parts of your event record.
    my %EventType = (0,'Error',2,'Warning',4,'Information', 8,'Audit success',16,'Audit failure'); $Win32::EventLog::GetMessageText =1; # Automatically call GetMessag +eText on each msg retrieved .... while ($limit--){ $EventLog->Read((EVENTLOG_SEQUENTIAL_READ|EVENTLOG_BACKWARDS_REA +D),0,$event); #to get a readable EventId #print "." if $limit % 100 == 0; $event->{'EventID'} = $event->{'EventID'} & 0xffff; $FindEventType and next unless $FindEventType == $event->{'Eve +ntID'}; Print_Event(); } .... ############################################# sub Print_Event{ my ($sec,$min,$hour,$mday,$mon,$year,$sday,$yday,$isdst) = loc +altime($event->{'TimeGenerated '}); $year += 1900; $mon +=1; # Zero-based if ($OptCSV){ print "$year/$mon/$mday $hour:$min,"; print $event->{EventID} . ","; $event->{'Strings'} =~ tr/\0/,/; print $event->{'Strings'} ."\n"; return; } print sprintf(" TimeStamp->%02d\-%02d\-%02d, %02d:%02d ",,$yea +r,$mon,$mday,$hour,$min); #readable EventType $event->{'EventType'} = $EventType{ $event->{'EventType'} }; #split the strings $event->{'Strings'} =~ tr/\0/\n/; # $event->{'Strings'} =~s/\s*\n*$//; # Zap trailing NewLines & +white-spaces.. foreach my $key (qw (EventType RecordNumber Category Source Ev +entID) )# { print sprintf( "%s->%s ",$key, $event->{$key} ); } print "\n\[$event->{Strings}\]\n"; #print "MESSAGE:\[$event->{Strings}\]\n"; } #############################################

         "An undefined problem has an infinite number of solutions." - Robert A. Humphrey         "If you're not part of the solution, you're part of the precipitate." - Henry J. Tillman

Re: Extracting useful information from Windows Event Logs
by Anonymous Monk on May 24, 2007 at 08:25 UTC
    Whats your question?
      Well from a text I need to extract certain information... I know the text will come after a certain partten..... for example "Document Wod.doc owned by DUSASAE was printed on HPLJ5 via port LPT1. Size in bytes: 37836; pages printed: 1" In the above... DUSASAE is my username which will always come after "owned by " and has a space before and after it, since my username can be variable length. How do I extract this sort of data. reading about regex ... but not really my skill yet.
        my $text="Document Wod.doc owned by DUSASAE was printed on HPLJ5 via p +ort LPT1. Size in bytes: 37836; pages printed: 1"; if ($text =~ /^Document\s+(\S+)\s+owned by\s+(\w+).+pages printed:\s+( +\d+)\z/) { print <<"EOT"; pages printed: $3 filename: '$1' username: $2 EOT }

        Appears to work for this simple case. But can the filename and username contain spaces. If so, what happens? Hard to say without a more precise spec. Feel free to adapt to your needs, though.

Re: Extracting useful information from Windows Event Logs
by Util (Priest) on May 25, 2007 at 04:14 UTC

    Before tye's reply made it redundant, I wrote a regex to parse the print message. FYI, it was:

    my $digits_re = qr{ \d+ }msx; my $words_re = qr{ \S+ (?: \s+ \S+)* }msx; my $print_message_re = qr{ \A Document [ ] ( $digits_re ) , [ ] ( $words_re ) [ ] owned [ ] by [ ] ( $words_re ) [ ] was [ ] printed [ ] on [ ] ( $words_re ) [ ] via [ ] port [ ] ( $words_re ) \. [ ]+ Size [ ] in [ ] bytes: [ ] ( $digits_re ) ; [ ] pages [ ] printed: [ ] ( $digits_re ) \s* \z }msx;

    Here is a cleaned-up and refactored solution to your problem.
    Working, tested code:

    #!/usr/bin/perl use strict; use warnings; use Win32::EventLog; use constant ONE_DAY => 24 * 60 * 60; my $yesterday = time() - ONE_DAY; my $wanted_date = format_date( $yesterday ); my $wanted_host = $ENV{ComputerName}; my $wanted_log = 'System'; my $wanted_source = 'Print'; print "Wanted Date : $wanted_date\n"; print "Wanted Host : $wanted_host\n"; print "Wanted Log : $wanted_log\n"; print "Wanted Source: $wanted_source\n"; my $handle = Win32::EventLog->new( $wanted_log, $wanted_host ) or die "Can't open '$wanted_log' EventLog from '$wanted_host'\n"; $handle->GetNumber( my $recs ) or die "Can't get number of EventLog records\n"; $handle->GetOldest( my $base ) or die "Can't get number of oldest EventLog record\n"; my $read_flags = EVENTLOG_FORWARDS_READ | EVENTLOG_SEEK_READ; foreach my $event_id ( $base .. ( $base + $recs - 1 ) ) { $handle->Read( $read_flags, $event_id, my $event_href ) or die "Can't read EventLog entry# $event_id\n"; next if $event_href->{Source} ne $wanted_source; my $event_date = format_date( $event_href->{'TimeGenerated'} ); next if $event_date ne $wanted_date; my ( $doc_num, $doc_name, $owner, $device, $port, $size, $pages ) = split "\x00", $event_href->{Strings}; printf( "%7d\t%15d\t%-15s\t%-7s\t%-23s\t%7d\t%s\n", $pages, $size, $owner, $port, $device, $doc_num, $doc_name, ); } sub format_date { die "Wrong number of arguments" if @_ != 1; my ($time) = @_; my ( $day, $month, $year ) = (localtime $time)[3..5]; return sprintf( '%04d-%02d-%02d', $year+1900, $month+1, $day ); }