Schrift
[thread]12697[/thread]

Daten aus Server-Logfile erheben (Common Log Format) mit Suchmuster



<< >> 10 Einträge, 1 Seite
mojo
 2008-10-30 00:01
#115842 #115842
User since
2008-10-29
4 Artikel
BenutzerIn
[default_avatar]
Hallo werte Community,

ich möchte ein Perl-Skript erstellen, welches mir aus einer Logdatei eines Servers(Proxy) die übertragene Datenmenge jeder geloggten IP-Adresse an einem bestimmten Tag (24h) herauszieht, die Datenmengen jeder IP aufaddiert und anschließend eine Art Statistik ausgibt, sortiert nach IP.

z.B. so:

IP: 192.168.0.178 Byte: 145552
IP: 192.168.0.211 Byte: 12121212
IP: 192.168.0.122 Byte: 999988 etc.

Die Logdatei ist im Common Log Format.

Also nach diesem Schema:

127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326

mich interessieren die beiden fettgedruckten Einträge (IP und Bytes)

Auszug:

Code: (dl )
1
2
3
192.168.0.1 - - [28/Oct/2008:11:10:27 +0100] "GET http://www.google.com/search?hl=de&lr=&client=iceweasel-a&rls=org.debian:de:unofficial&ie=UTF-8&oe=UTF-8&q=lpx+formfaktor+design+jpg&start=30&sa=N HTTP/1.1" 200 6199 "http://www.google.com/search?hl=de&lr=&client=iceweasel-a&rls=org.debian:de:unofficial&ie=UTF-8&oe=UTF-8&q=lpx+formfaktor+design+jpg&start=20&sa=N" "Mozilla/5.0 (X11; U; Linux x86_64; de; rv:1.8.1.16) Gecko/20080702 Iceweasel/2.0.0.16 (Debian-2.0.0.16-0etch1)"
192.168.0.131 - - [28/Oct/2008:11:10:28 +0100] "POST http://mail.google.com/mail/channel/bind?at=xn3j35vkipm3ag8wbq29v4hr0ebgm1&VER=6&it=7200021&SID=7D0F626A0A21187&RID=31563&zx=ohytef6xzilb&t=1 HTTP/1.1" 200 26 "http://mail.google.com/mail/?ui=2&view=js&name=js&ver=GikK_XiN2lg&am=X_E4tcT3MCGpBf3gi728Yw" "Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_5_4; de-de) AppleWebKit/525.18 (KHTML, like Gecko) Version/3.1.2 Safari/525.20.1"
192.168.0.129 - - [28/Oct/2008:11:10:47 +0100] "POST http://207.46.109.80/gateway/gateway.dll?Action=poll&SessionID=579256175.2115166193 HTTP/1.0" 200 - "-" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1; SV1; .NET CLR 2.0.50727; .NET CLR 3.0.04506.648; .NET CLR 3.5.21022; Windows Live Messenger 8.5.1302)"


Ein entsprechendes Suchmuster habe ich mit bereits erstellt, dieses funktioniert auch.
Mit einer Schleife habe ich IP und Bytes in Skalare einlesen lassen und kann diese ausgeben lassen, so wie in der o.g. Liste.

Mir ist klar, das dies nicht reicht. Ich müsste alle IPs und dazugehörige Datenmengen via Hash oder Array(s) ablegen und bei immer wieder auftauchenden IPs die Bytes aufaddieren lassen.

Vorher müsste geprüft werden, ob die IP bereits erfasst wurde und ob überhaupt Daten übertragen wurden.

Da es nicht sehr viele zu erfassende IPs sind, sollte am Ende eine recht übersichtliche Auswertung herauskommen.


Einige (vielleicht auch viele :) ) mögen die Lösung dieses Problems vielleicht als Pillepalle empfinden, ich jedoch wäre schon für die ein oder andere Anregung hierzu sehr dankbar.

Wie würdet Ihr (schrittweise) herangehen ?


Grüße
Dubu
 2008-10-30 00:34
#115846 #115846
User since
2003-08-04
2145 Artikel
ModeratorIn + EditorIn

user image
Eigentlich hast du doch schon alles gesagt ...
Code (perl): (dl )
1
2
3
4
5
6
7
8
9
10
11
12
my %daten;
while (...) { # Einlesen
    # Auswerten der Zeile, IP in $ip, Bytes in $bytes
    ...
    # Addieren:
    $daten{$ip} += $bytes;
}

# Ausgabe Datenmenge nach IP
for my $ip ( sort keys %daten) {
    print "IP: $ip\tBytes: $daten{$ip}\n";
}
pq
 2008-10-30 00:37
#115847 #115847
User since
2003-08-04
12208 Artikel
Admin1
[Homepage]
user image
also wenn du die schleife schon hast:
Code (perl): (dl )
1
2
3
4
5
6
use Data::Dumper;
my %bytes;
while (...) { # deine schleife
    $bytes{ $ip } += $bytes;
}
print Dumper \%bytes;

das sollte ein anfang sein.
zum umgehen mit hashes und anderen strukturen:
http://p3rl.org/perldata
http://p3rl.org/perldsc
Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live. -- Damian Conway in "Perl Best Practices"
lesen: Wiki:Wie frage ich & perlintro Wiki:brian's Leitfaden für jedes Perl-Problem
mojo
 2008-10-30 08:13
#115852 #115852
User since
2008-10-29
4 Artikel
BenutzerIn
[default_avatar]
Danke!

Was bedeutet in diesem Zusammenhang:

Code: (dl )
use Data::Dumper;


und

Code: (dl )
 print Dumper \%bytes;


Ich habe bisher nur mit Skalaren, Arrays und Hashes gearbeitet.

Grüße
nepos
 2008-10-30 09:18
#115853 #115853
User since
2005-08-17
1420 Artikel
BenutzerIn
[Homepage] [default_avatar]
Naja, damit wird das Data::Dumper-Modul geladen. Dieses stellt die Funktion Dumper() zur Verfügung, mit der man verschachtelte Datenstrukturen recht gut ausgeben kann.
Zum Debuggen sehr praktisch.
mojo
 2008-11-07 20:14
#116072 #116072
User since
2008-10-29
4 Artikel
BenutzerIn
[default_avatar]
so ...

das Perl-Skript steht jetzt.

Es funktioniert auch alles, so wie ich das wollte. Bis auf eine Sache ...

Es grast zunächst die Logdatei (Common Log Format wie gesagt) ab und fischt aus jeder Zeile die Datumsangabe heraus.
Im folgenden Format: z.B.

29/Sep/2008
04/Okt/2008
06/Nov/2008 etc. pp.

und legt diese in einem Hash (%tage) mit diesen Datumsangaben als Keys ab. Die Fächer werden mittels Schleife und Zählvariable mit Nummern (beginnend mit 1 gefüllt.

sieht dann etwa so aus:
Code (perl): (dl )
1
2
3
4
5
 %days = (
        26/Sep/2008 => '1',
        27/Okt/2008 => '2',
        28/Nov/2008 => '3',
);

Die Nr.-Angaben werden für eine spätere Benutzerauswahl des Datums genutzt (dafür wird parallel eine Array mit allen Datumsangeben gefüllt, ins erst Fach kommt eine 0, sodass die Datumseinträge mit den gleichen Nummern (Fachnummern 1,2,3 und soweiter entsprechend den Inhalten der Hashfächer)angewählt werden können)

Die sortierte Ausgabe erfolgt dann so:
Code (perl): (dl )
1
2
3
4
for my $datum (sort keys %days){        #Reihenfolge der Keys des Hash's '%tage' nach Datumseinrägen sortieren
                print "$datum\t($tage{$datum})\n";      #sortierte Ausgabe des Hash's %tage
}
print "\nWelches Datum auswerten? (Nr. eingeben): ";    #Eingabeaufforderung f. Auswahl 


Dies klappt wunderbar, solange es Datumseinträge eines Monats sind - dann sieht die Ausgabe z.B. so aus:

Code: (dl )
1
2
3
05/Oct/2008       (1)
06/Oct/2008 (3)
07/Oct/2008 (4)


Blöd wirds, wenn Einträge mehrerer Monate auftauchen, dann sieht die ausgegebene Liste z.B. so aus:
Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
05/May/2008       (5)
05/Jul/2008 (7)
05/Sep/2008 (9)
05/Oct/2008 (10)
06/Feb/2008 (3)
06/Mar/2008 (4)
06/Jul/2008 (8)
06/Oct/2008 (11)
07/Mar/2008 (12)
07/Apr/2008 (2)
07/Sep/2008 (16)
07/Oct/2008 (12)
08/Dec/2008 (25)
09/Dec/2008 (26)
10/Dec/2008 (27)


Kauderwelsch.
Sprich: es wird zwar sortiert, aber falsch: nur nach den Tageszahlen (erste beide Ziffern)
Ich möchte jedoch, dass sowohl die Reihenfolge der Tage, Monate und eventuell auch der Jahre stimmt.

Ich würde die Datums-Strings zunächst erst einmal mittels Suchmuster (Reg. Ausdruck) in Tag, Monat und Jahr zerhacken. z.B. so:

Code (perl): (dl )
1
2
3
4
5
if ($ueb=~m/^(\d+)\/(\w+)\/(\d+)$/){    #Aufsplitten des Datums-Strings in Tag, Monat, Jahr
                        $d=$1;                          #Zuweisen in entsprechende Skalare
                        $m=$2;
                        $y=$3;
}


Aber wie weiter? man müsste ja erstmal eine gültige Monatsreihenfolge Festlegen, oder gibt es da vielleicht schon eine Funktion/ Modul im Perl?

Dann könnte man ja eventuell mit einer solchen Schleife weitersortieren:

Code (perl): (dl )
1
2
3
4
for my $m (sort keys %days){    #Reihenfolge der Keys des Hash's '%tage' nach Monatseinträgen sortiern
                print "$datum\t($tage{$m})\n";  #sortierte Ausgabe des Hash's %tage
}
print "\nWelches Datum auswerten? (Nr. eingeben): ";    #Eingabeaufforderung f. Auswahl 


Aber man müsste doch auf jeden Fall mehrere neue Hashes anlegen. Es wird doch auf jeden Fall eine recht komplizierte Datensstruktur, oder?

z.B.

1. alle Tage geordnet den entsprechenden Monaten zuweisen
2. die "gefüllten" Monate sortieren
3. die sortierten Monate den entsprechenden Jahren zuweisen

4. Das ganze ordentlich sortiert und durch den Benutzer anwählbar ausgeben.

Wie würdet Ihr dies am sinnvollsten bewerkstelligen?

Möglich, dass es eine vorgefertigte Perl-Funktion gibt und ich Eulen nach Athen trage.

Interessiert mich sehr!
topeg
 2008-11-07 22:32
#116081 #116081
User since
2006-07-10
2611 Artikel
BenutzerIn

user image
Versuch mal das:

Code (perl): (dl )
1
2
3
4
5
6
my %m=(Jan=>1, Feb=>2, Mar=>3, Apr=>4,  May=>5,  Jun=>6,
       Jul=>7, Aug=>8, Sep=>9, Oct=>10, Nov=>11, Dec=>12);
sub datesort
{ return $a->[3] <=> $b->[3] || $m{$a->[2]} <=> $m{$b->[2]} || $a->[1] <=> $b->[1]; }

my @list=map{$_=$_->[0]}sort{datesort()}map{$_=[$_,(split(/\//,$_))]}keys(%days);


Das sortiert nach Jahr - Monat - Tag.
Dubu
 2008-11-08 12:59
#116098 #116098
User since
2003-08-04
2145 Artikel
ModeratorIn + EditorIn

user image
Wenn dein Logfile bzgl. Datum monoton steigend ist (d.h. es kommt nie ein älteres Datum nach einem neueren), dann würde ich für %date keinen Hash nehmen sondern gleich nur ein Array.
Code (perl): (dl )
1
2
3
4
5
6
7
8
9
10
11
12
my @date;
...
while (my $zeile = <$logfile>) {
    # Datum herausfiltern
    if ($zeile =~ /.../) {
        $datum = $1;      # so etwas wie "12/Dec/2007"
        if (@date == 0 || $date[-1] ne $datum) {  # @date leer oder neues Datum
            push @date, $datum;
        }
        ...
    }
}

(ungetestet)
Es wird einfach verglichen, ob das gerade eingelesene Datum ungleich dem letzten gespeicherten ist - oder ob das Array noch leer ist. Wenn ja, kommt das Datum ins Array.

Alternativ zu dem oben, oder wenn das mit der Sortierung im Logfile nicht gilt, kannst du auch CPAN:Tie::IxHash nehmen. Damit bekommst du einen Hash, der die Reihenfolge erhält, in der Elemente hinzugefügt werden.
pq
 2008-11-08 14:19
#116101 #116101
User since
2003-08-04
12208 Artikel
Admin1
[Homepage]
user image
topeg+2008-11-07 21:32:47--
Code (perl): (dl )
my @list=map{$_=$_->[0]}sort{datesort()}map{$_=[$_,(split(/\//,$_))]}keys(%days);

ach du meine güte. ein vorteil von perl ist, dass man vieles kürzer hinkriegt als in anderen
sprachen, aber man muss dazu nicht auch noch jedes leerzeichen und newline weglassen. wer soll
das denn lesen? wie findet man in dem code einen eventuellen bug? schau mal:
Code (perl): (dl )
1
2
3
4
5
6
7
my @list = map {
    $_->[0]
} sort 
    datesort
map {
    [ $_ , split /\//, $_ ]
} keys %days;

ist das nicht lesbarer?
ausserdem hatte dein code wirklich einen fehler, der monat nummer 10 hiess im beispiel "Okt",
aber in deinem hash "Oct".

edit: ausserdem ist die zuweisung an $_ in map völlig überflüssig, du willst ja hier nicht das
originalarray verändern, denn es gibt kar kein originalarray. map liefert für jedes ihm übergebene
element das zurück, was der code-block zurückliefert.
Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live. -- Damian Conway in "Perl Best Practices"
lesen: Wiki:Wie frage ich & perlintro Wiki:brian's Leitfaden für jedes Perl-Problem
mojo
 2008-11-09 14:08
#116129 #116129
User since
2008-10-29
4 Artikel
BenutzerIn
[default_avatar]
Hey, Danke für die Anregungen:)

Werde mir das mal ansehen.

Quote
Code (perl): (dl )
1
2
3
4
5
6
7
1: my @list = map {
2:     $_->[0]
3: } sort 
4:     datesort
5: map {
6:     [ $_ , split /\//, $_ ]
7: } keys %days;

ist das nicht lesbarer?

Ist auf jeden Fall besser lesbar.
<< >> 10 Einträge, 1 Seite



View all threads created 2008-10-30 00:01.