GUI mit Perl/Tk

Apache Logs mit Tk und POE verfolgen

Neuste Einträhe in den Apache-Logs anzeigen

So kann man Apache-Logs über ein Tk-Interface überwachen. Zur Erzeugung von parallelen Prozessen wird hier POE verwendet.

Apache-Logs in Perl/Tk-GUI beobachten

#!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

Literatur und Quellen

  1. POE-Kochbuch
  2. POE+Tk-Rezept
  3. POE+Wheel::FollowTail - Rezept
Top