So kann man Apache-Logs über ein Tk-Interface überwachen. Zur Erzeugung von parallelen Prozessen wird hier POE verwendet.
#!perl
=head1 apacheLogParser_tk.pl - ein Programm zum Überwachen von Dateien
=head2 VERWENDUNG
Die zu überwachenden Dateien in @logdateien eintragen, Programm starten.
=cut
use warnings;
use strict;
use Config::Auto;
use Data::Dumper qw/Dumper/;
use File::Spec;
use vars qw($VERSION);
$VERSION = 0.2;
# Tk support is enabled if the Tk module is used before POE itself.
use Tk;
use POE qw/Wheel::FollowTail/;
# Definiere globale Variablen zum Programm
# ToDo: Konfigurationsdatei verwenden, Dialog zum hinzufügen und Entfernen der
# Dateien anlegen.
my @logdateien = (
File::Spec->catfile(qw(d: Apache logs error.log)),
File::Spec->catfile(qw(d: Apache logs access.log)),
);
# Konfiguration für die Anzeige der Logs.
# Wenn was zurück kommt wird das in die Einagbe geschrieben,
# wenn 0 zurück kommt nicht.
my %dateieinstellungen = (
$logdateien[0] => {
myfunc => sub{
my $text = shift; # die gelesene Text-Zeile
my $textw = shift; # Text-Widget
$text .= "\n";
# Timestampd und dergleichen entfernen
$text =~ s/^\[.+\]//;
# Zeilen aufteilen in Fehlermeldung und Fehlerquelle, und umbrechen
$text =~ s/\, (referer\: http\:\/\/)/\n\t$1/;
# Fehlermeldung rot markieren.
# Den zu markierenden roten Teil auslesen, das STück davor einfügen,
# das gefundene Stück rot einfügen, das Stück danach einfügen.
# AP_tiny.cgi: Can't locate CMS/ADMIN/AP_tiny.pm in @INC (@INC contains: ../../pm
# C:/Apache/cgi-bin/cms/admin C:/Perl/site/lib C:/Perl/lib .) at
# C:/Apache/cgi-bin/cms/admin/AP_tiny.cgi line 9.
if( $text =~ m/(.+:)(.+)( at.+)/ ) {
$textw->insert('end',$1);
$textw->insert('end',$2,'red_text');
$textw->insert('end',$3);
$textw->tagConfigure('red_text', -foreground => 'red');
}else{
# ansonsten einfach den ganzen Text einfügen
$textw->insert('end',$text);
}
return 0;
},
},
);
# Dubletten entfernen
my %seen = ();
@logdateien = grep { ! $seen{ $_ }++ } @logdateien;
undef %seen;
foreach( @logdateien ) {
die "Logdatei [$_] existiert nicht!\n" unless -e $_;
}
# Create the session that will drive the user interface.
POE::Session->create
( inline_states =>
{ _start => \&ui_start,
ev_count => \&ui_count,
ev_clear => \&ui_clear,
got_line => \&updateLog_line,
got_error => \&updateLog_error,
},
args => \@logdateien,
);
# Run the program until it is exited.
$poe_kernel->run();
exit 0;
=head2 METHODEN
=head3 ui_start
Baut die grafische Oberfläche zusammen. Pro angegebene Log-Datei gibt es einen
Notebook-Reiter.
ToDo: Wenn die Reiter breiter sind als das Fenster sollte ein Scroll-Teil
an der Seite erscheinen, oder irgend eine andere Möglichkeit geboten werden
auf diese Reiter zuzugreifen.
Reiter scheinen sowieso irgendwie ungeeigenet zu sein, einzelne Fenster wären
wohl besser.
=cut
sub ui_start {
my ( $kernel, $session, $heap ) = @_[ KERNEL, SESSION, HEAP ];
# -- Fenstergröße ungefähr anpassen
$poe_main_window->configure(-width => 1280, -height => 480,);
$poe_main_window->packPropagate(0);
# -- TK-GUI erstellen
require Tk::NoteBook;
$heap->{notebook} = $poe_main_window->NoteBook();
foreach my $logdatei ( @logdateien ) {
$heap->{notebookSeiten}{$logdatei}{seite} = $heap->{notebook}->add($logdatei,
-label => $logdatei,);
$heap->{notebookSeiten}{$logdatei}{scr_txt} = $heap->{notebookSeiten}{$logdatei}{seite}->Scrolled(
# keine Eigenschaften wie Höhe oder Breite, weil das Textfeld skalierbar sein soll.
Text => -scrollbars => 'se',
)->pack(-expand => 1, -fill => 'both',);
# -- Buttons für Funktionen erstellen
$heap->{notebookSeiten}{$logdatei}{btn_frame} = $heap->{notebookSeiten}{$logdatei}{seite}->Frame();
$heap->{notebookSeiten}{$logdatei}{clear_btn} = $heap->{notebookSeiten}{$logdatei}{btn_frame}->Button(
-text => 'Anzeige leeren',
-command => sub{
$heap->{notebookSeiten}{$logdatei}{scr_txt}->Subwidget('scrolled')->delete("0.0", "end");
#print "$logdatei\n";
#print Dumper $heap->{notebookSeiten}{$logdatei}{scr_txt}->Subwidget('scrolled'); # = undef???
},
)->pack(-side => 'left',);
$heap->{notebookSeiten}{$logdatei}{btn_frame}->pack(-side => 'left',);
}
$heap->{notebook}->pack(-expand => 1, -fill => 'both',);
# -- Statusbar
# -> für den UI-Counter
require Tk::StatusBar;
$heap->{statusbar} = $poe_main_window->StatusBar();
$heap->{statusbar}->addLabel(
-relief => 'flat',
-text => "Welcome to the statusbar",
);
$heap->{statusbar}->addLabel(
-text => 'Frame:',
-width => '10',
-anchor => 'center',
);
$heap->{statusbar}->addLabel(
-width => 20,
-anchor => 'center',
-textvariable => \$heap->{counter},
-foreground => 'blue',
);
$heap->{statusbar}->addLabel(
-width => 10,
-anchor => 'center',
-text => "Clear",
-foreground => 'blue',
-command => $session->postback("ev_clear"),
-event => '<Button-1>',
);
# -- Wheel erstellen um die Error.log zu überwachen
foreach( @logdateien ) {
$heap->{wheel}->{$_} = POE::Wheel::FollowTail->new(
Filename => $_,
InputEvent => 'got_line',
ErrorEvent => 'got_error',
SeekBack => 1024,
);
}
$heap->{first} = 0;
# Loop starten
$kernel->yield("ev_count");
} # /ui_start
=head3 ui_count
Handle the "ev_count" event by increasing a counter and displaying
its new value.
=cut
sub ui_count {
$_[HEAP]->{counter}++;
$_[KERNEL]->yield("ev_count");
} # /ui_count
=head3 ui_clear
Handle the "ev_clear" event by clearing and redisplaying the counter.
=cut
sub ui_clear {
$_[HEAP]->{counter} = 0;
} # /ui_clear
=head3 updateLog_line
Fügt die neuen Informationen aus den zu verfolgenden Dateien im entsprechenden
Reiter an. In $_[ARG0] scheint dabei das zu stehen, was bei der gerade modifizierten
Datei angehangen wurde und in $_[ARG1] die Nummer (beginnend bei 1) der
Datei in der Liste der zu verfolgenden Dateien ( siehe args - Argument bei
POE::Session->create() ).
Ideen: Flashen der Reiter wenn ein Update erfolgt ist. Automatisches Scrollen
als Option (an|aus), umdrehen der einzelnen Datei-Abschnitte (geht z.B. bei
Apache-Logdateien) um den aktuellen Eintrage immer oben zu haben.
=cut
sub updateLog_line {
my ( $kernel, $session, $heap ) = @_[ KERNEL, SESSION, HEAP ];
my $eingabe = $_[ARG0];
#print STDERR Dumper $_[ARG0];
my @eingaben = split m/\\r, /, $eingabe;
foreach my $eing ( @eingaben ) {
# Wenn ein Filter für die Datei hinterlegt wurde wird die
# Eingabe da durch gejagt.
if( exists $dateieinstellungen{$logdateien[($_[ARG1]-1)]} ) {
$eing = $dateieinstellungen{$logdateien[($_[ARG1]-1)]}->{myfunc}->(
$eing,
$heap->{notebookSeiten}{$logdateien[($_[ARG1]-1)]}{scr_txt}
);
next if $eing eq 0;
}
$heap->{notebookSeiten}{$logdateien[($_[ARG1]-1)]}{scr_txt}->insert(
"end",
"normal: $eing\n"
) if $heap->{first}++;
}
} # /updateLog_line
=head3 updateLog_error
Keine Ahnung was das hier macht...
=cut
sub updateLog_error {
warn "$_[ARG0]\n";
} # /pdateLog_error