Python: Apache Log Einlesen und Filtern

Gast #4965831
Lesenswert?

Hey,

ich brauche einmal eure Hilfe.

Ich muss ein Apachelog einlesen und als Ausgabe brauche ich die Anzahl 
der 200er, 300er, 400er und 500er Meldungen.
Ich habe schon Stunden damit verbracht aber mir fehlen Regex Kenntnisse.
Danke für eure Hilfe.


Beispiel Ausgabe:

Fehlercodes
~~~~~~~~~~~
200: 21344
300: 14353
400: 864
500: 5

Die Gesamten Zeilen des Logs sind wie folgt aufgebaut.:
1
172.16.11.12 - - [26/May/2013:14:06:05 +0200] "GET /navleiste/i_xyz_navleiste.gif HTTP/1.1" 200 1348
Gast #4966131
Lesenswert?

Der folgende Code sollte das machen, bitte beachten, er ist nicht durch 
getestet und etwas arm an Fehlerbehandlung.

1
#!/usr/bin/env python3
2
# -*- coding: utf-8 -*-
3
# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
4
from collections import Counter
5

6
def statuscnt(fname):
7
    result = Counter()
8
    with open(fname) as fin:
9
        for line in fin:
10
            code = line.split()[-2]
11
            status[code] += 1
12

13
    # Ausgabe
14
    print('Fehlercodes')
15
    for status, count in sorted(result.items()):
16
        print('{0}: {1}'.format(status, count)

der Haupttrick hier ist code = line.split()[-2] welches die logzeile des 
Access log nimmt und bei den Leerzeichen in eine Liste teilt
Weiter geht die Zeile davon aus dass, das vorletzte element der Liste 
(-2) der Statuscode ist. Hier solltest du ggf. noch eine Plausibilität 
Prüfung für die Annahme machen (z.b.) Probieren ob int(code) eine 
Execption wirft.

Der zweite nette Trick ist die Counter-Klasse zu verwenden welche bei 
status[code] eine 0 liefert wenn Code noch nicht bekannt ist. deshalb
geht status[code] += 1 gut selbst wenn das noch nicht definiert ist.
Gast #4966518
Lesenswert?

PythonBob schrieb:
> Danke für deine Hilfe. :)
>
> Ich bekomme nur leider folgende Meldung.:
> SyntaxError: unexpected EOF while parsing

eine Zeilennummer wäre toll :)

>   print('{0}: {1}'.format(status, count)

hier fehlt eine schließende Klammer

print('{0}: {1}'.format(status, count))


hoffe das war es.
Gast #4966628
Lesenswert?

Das Open wird schon in der Funktion gemacht also Datei ist nur der path 
ohne open und dann die Funktion aufrufen.
1
def statuscnt(Datei):
2
[....]
3

4
Datei = 'C:\\Users\\XXXX\\Destop\\apache.log' 
5
statuscnt(Datei)
Gast #4972684
Lesenswert?

PythonBob schrieb:
> Nochmal Danke aber ich hab enoch eben eine Frage.:
>
> Wie funktioniert das ganze denn wenn ich mehrere Logs einlesen möchte
> und die Statistik für diese zusammen erstellen will?

Ich werde nicht deine Hausaufgabe machen :)

hier ein paar Hinweise.

1.) du kannst die Funktion pro Logfile Aufrufen
2.) die Funktion sollte vielleicht nicht selbst die Ausgabe machen 
sondern das result Objekt zurück geben.
3.) Das result Objekt (Counter Klasse) hat eine update-Methode, welche 
als Parameter eine andere Instanz der Counter Klasse akzeptiert und die 
Addition pro Eintrag für dich macht.

Mit den Hinweisen solltest du das Programm ändern können. Wenn du 
Probleme hast oder Hilfe brauchst kannst du gerne wieder fragen aber 
erstmals müssen nun ein paar Zeilen Code von dir kommen.
Gast #4973023
Lesenswert?

@imonbln

Ich will ja auch nicht, dass du hier alles für mich schreibst, solche 
Tipps sind völlig ausreichend.
Und du machst da auch nicht meine Hausaufgaben oder so weil das nur ein 
kleiner Teil von einem größerem Projekt ist. ^^


Ich habe versucht deine Tipps umzusetzen und bin schon etwas weiter 
gekommen.
Die Ausgabe sieht es wie folgt aus.:
1
200: Counter({'200': 43230, '304': 27530, '401': 974, '303': 720, '404': 630, '302': 484, '206': 202, '406': 60, '207': 54, '301': 32, '403': 16, '405': 14, '501': 14, '500': 10, '204': 2})
2
204: Counter({'200': 43230, '304': 27530, '401': 974, '303': 720, '404': 630, '302': 484, '206': 202, '406': 60, '207': 54, '301': 32, '403': 16, '405': 14, '501': 14, '500': 10, '204': 2})

Also ich habe es quasi geschaft Zwei Log Dateien geminsam auszuweiten 
und die Fehleroces zusammenzurechnen aber die Ausgabe ist jetzt halt in 
einem recht komischen Format.

Das ist mein Code
1
from collections import Counter
2

3

4
def statuscnt(DateiA):
5
    result1 = Counter()
6
    with open(DateiA) as fin:
7
        for line in fin:
8
            code = line.split()[-2]
9
            result1[code] += 1
10

11
    result2 = Counter()
12
    with open(DateiB) as fin:
13
        for line in fin:
14
            code = line.split()[-2]  #
15
            result2[code] += 1
16
    for status, count in sorted(result2.items()):
17
        sum = result1 + result2
18
        print('{0}: {1}'.format(status, sum))
19

20
DateiA = 'C://Users//XXX//Desktop//apache.log'
21
DateiB = 'C://Users//XXX//Desktop//apache.log'
22
statuscnt(DateiA)

Wahrscheinlich ist der Fehler super offensichtlich aber ich habe es 
nicht besser hibekommen. :P
Gast #4973147
Lesenswert?

PythonBob schrieb:
> Ich habe versucht deine Tipps umzusetzen und bin schon etwas weiter
> gekommen.

und nicht einen davon in dein Code eingebaut ;)

PythonBob schrieb:
> result2 = Counter()
>     with open(DateiB) as fin:
>         for line in fin:
>             code = line.split()[-2]  #

das open klappt nur weil DateiB eine globale Variable ist. hier hätte 
ich erwartetet das du statt dessen zweimal die funktion statuscnt 
aufrufst einmal mit DateiA und einmal mit DateiB. Dein Copy&Paste Ansatz 
wird bei 20 Dateien Stressig.
1
 
2
def statuscnt(DateiA):
3
    result1 = Counter()
4
    with open(DateiA) as fin:
5
        for line in fin:
6
            code = line.split()[-2]
7
            result1[code] += 1
8
    return result

wäre mein Vorschlag für die Funktion.

PythonBob schrieb:
> sum = result1 + result2
>         print('{0}: {1}'.format(status, sum))

das ist wie du schon bemerkt hast nicht das was du willst, hier wäre 
Hinweis 3 dein Freund.
1
 gesamt = Counter()
2
 [ für jede datei ]
3
   gesamt.update(teilergebnis) 
4
 # Ausgabe von gesamt
#4973239
Lesenswert?

PythonBob schrieb:
> Ich habe versucht deine Tipps umzusetzen und bin schon etwas weiter
> gekommen.

Die Idee ist, den Code zu verstehen. Vielleicht magst Du die 
Dokumentation von collections.Counter und und str.split lesen?

> Wahrscheinlich ist der Fehler super offensichtlich aber ich habe es nicht
> besser hibekommen. :P

Mehr als offensichtlich... ;-)

Tipp 0: statuscnt() kann die Counter-Instanz zurückgeben.
Tipp 1: Counter-Instanzen kann man einfach addieren (_add_).
Tipp 2: argparse.ArgumentParser benutzen:
1
parser = ArgumentParser(description='Parse Apache Logs')
2
parser.add_argument('filenames', nargs='+', help='Logs')
3
args = parser.parse_args()
4
for filename in args.filenames:
5
    ...

HTH,
Sheeva
Gast #4974422
Lesenswert?

@imonbln
@Sheeva Plug


Sooooo ich habe das ganze jetzt wie folgt gelöst.: (ist aber auf zwei 
Dateien beschränkt aber erstmal ausreichend)

Weiter oben in meinem Code ist eine Abfrage ob man eine weitere Datei 
einlesen möchte und  "user_input_3" ist dann der Dateipfad.

1
result = Counter()
2
    while True:
3
#Für eine Datei
4
        if user_input_2 == 'Nein':
5
            def statuscnt(DateiA, result):
6
             ...
7
            DateiA = user_input
8
            statuscnt(DateiA, result)
9
            break
10

11
#Für zwei Dateien
12
        elif user_input_2 == 'Ja':
13
            def statuscnt2(DateiA, DateiB, result):
14
               with open(DateiA) as fin:
15
                ...
16
               result.update(result)  
17
               for status, count in sorted(result.items()):
18
                   print('{0}: {1}'.format(status, count)) 
19

20

21
            DateiA = user_input
22
            DateiB = user_input_3
23
            statuscnt2(DateiA, DateiB, result)
24
            break
Das funktionert soweit ohne Probleme
Nochmal Danke für eure Unterstützung :)
Gast #4974607
Lesenswert?

PythonBob schrieb:
> if user_input_2 == 'Nein':
>             def statuscnt(DateiA, result):
>              ...

Das ist gefährlich innerhalb einer if Bedingung eine Funktion zu 
deklarieren. Sowas sollte man nicht machen. Das fällt dir auf die Füße.

Ausserdem verstehe ich nicht warum du für denn fall eine Datei eine 
andere Funktion brauchst als für den fall von mehren Dateien.

Die Übergabe des result Parameter an deine Funktion erschließt sich mir 
nicht. Ich denke das ist überflüssig aber vielleicht verbirgt sich in 
dein [...] eine interessante Logik dazu

PythonBob schrieb:
> statuscnt(DateiA, result)
>             break

Für beide falle kannst du die gleiche Funktion verwenden.

PythonBob schrieb:
> Sooooo ich habe das ganze jetzt wie folgt gelöst.: (ist aber auf zwei
> Dateien beschränkt aber erstmal ausreichend)

Nein, mit der Funktion wie ich sie Dir in im letzten Post geschrieben 
habe mit dem Tipp2 von Sheeva Plug kannst du n-Dateien auf einmal 
verarbeiten.

hier nochmal einen Tipp für n-Dateien.
1
gesamt = Counter()
2
for filename in args.filenames:
3
    teilerebnis = statuscnt(filename)
4
    gesamt += teilergbnis
5
#Ausgabe


Generell habe ich gegenwärtig nicht den Eindruck das du spezielle Python 
Probleme hast, ich denke dir fehlt eher noch etwas Programmiererfahrung. 
Ich würde dir zum Beispiel empfehlen, Dir nochmal klar zu machen was 
Funktionen sind und wie man diese Benutzt (ob in Python oder was anderes 
spielt keine Rolle).

Ansonsten wird das schwer, dir helfen zu können, denn für mich (und 
sicher auch andere) steht hier im Thread seit gestern (mit den Tipps von 
Sheeva Plug und mir) das Komplette Programm welches du schreiben willst.
Du musst eigentlich nur noch (TM) die Puzzelteile verbinden.
Natürlich ist das für ein Anfänger schwer, aber aufgrund deiner 
scheinbar geringen Programmiererfahrung, ist es auch nicht gerade leicht 
dir zu helfen ohne dir die Lösung hinzuschreiben.
Daher mein Tipp nimm dir nochmal dein Programm vom 07.04 welches eine 
Datei  Parsen kann, gehe den Thread nochmal durch und erweitere es in 
dieser Reihenfolge

1.) schreibe die funktion statuscnt so um das sie mit return das Counter 
Objekt zurück gibt.

2.) ändere den Aufruf der Funktion statuscnt so das dieser den return 
wert von statuscnt speichert.

3.) nimm den return wert von statuscnt (Tipp die Variable von 2.) und 
gebe es aus (tipp die Ausgabe macht jetzt, Stand 07.04 noch die Funktion 
statuscnt)

4.) Überlege wie du nun mehr als eine Datei ausgeben kannst ( Tipp die 
Funktion statuscnt muss spätestens nach 3. nicht mehr geändert werden)

HTH

imonbln

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren