Elemente aus einem Logfile zur Suche in einem anderen verwenden

Wenn du dir nicht sicher bist, in welchem der anderen Foren du die Frage stellen sollst, dann bist du hier im Forum für allgemeine Fragen sicher richtig.
fredvonfat
User
Beiträge: 12
Registriert: Mittwoch 12. September 2018, 10:00
Wohnort: Berlin

Mittwoch 12. September 2018, 12:31

Hallo zusammen,

ich bin Python Neuling und zwar aus der Not heraus geboren. Ich betreue ein System, welches mir zwei relevante Logfiles liefert.
Das erste ist eine Kurzfassung um ein Überblick zu erhalten und das zweite ist das, in dem jede Kleinigkeit mitgeschrieben wird.
Es geht bei dem System um Kommunikation DTMF-Ton basierter Geräte. im Zuge der Umstellung der Telefonnetze auf Voip, kommt es zu Fehlkommunikation zwischen den Gräten. Ziel meines Projektes ist, die Kommunikation zweier Geräte aus dem Logfile zu separieren und über einen gewissen Zeitraum zu dokumentieren / auszuwerten.

Bislang habe ich dazu ein Debian 9 System aufgesetzt, das sich täglich die Logfiles kopiert. Anhand von Shell-Skripten habe ich schon ein paar Erfahrungen mit den Eigenarten der beiden Logfiles sammeln können, allerdings bin ich dort an Grenzen gestoßen, die ich hoffe mit Python überwinden zu können.

Logfile 1 (log_file) wird immer am Ende einer gelungenen bzw. misslungenen Kommunikation geschrieben.

Es sollen, die Zeilen mit "ISDN Fehlanruf" extrahiert werden, diese sehen, wie folgt aus:

1620 31/08/18 00:09:36 ISDN Fehlanruf C:00 xxxx xxxxxxxxxxx Diag:DEKODIERUNGSFEHLER ,
^^^^ ^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^ ^^^^ ^^^^^^^^^^^^^^
Lfd. Zeitstempel Beschreibung Duwa Rufnummer

und daraus folgende Angaben zur Weiterverarbeitung entnommen werden:
1. Datum im Format Jahr-Monat-Tag plus entsprechendem Zeitstempel,
2. Die Durchwahlnummer (Duwa),
3. Die Rufnummer (11 stellig, manchmal auch 10stellig)

Diese Angaben sollen im weiteren Verlauf, dazu Verwendet werden, die entsprechende Kommunikation aus dem zweiten Logfile zu isolieren.

Bislang habe ich ein Skript geschrieben, welches mit der Angabe des Datums aufgerufen wird und mir die gewünschten Parameter aus dem Logfile in die Standardausgabe schreibt.

Wie kann der von mir konstruierte Ausgabe String evtl. vereinfacht werden?
Die "print" Anweisung funktioniert, die "write" Anweisung leider nicht mit der gleichen Syntax. Worin muss sich die Syntax zwischen print und write unterscheiden, so dass mein konstruierter String in der write Anweisung funktioniert?
Wie kann ich folgendes Skript eleganter gestalten?

#!/usr/bin/python
#
#
import sys

Datum = sys.argv[1]

err = "ISDN Fehlanruf"
log_path = "/var/log/ESI/ESI2/"
log_file = "F1log_"
write_path = "/home/marc/python/"
write_file = "PY_Auswertung.txt"

fobj_in = open(log_path + log_file + Datum + ".txt")
fobj_out = open(write_path + write_file ,"w")

for line in fobj_in:
if err in line:
print ("20" + line[11:13] + "-" + line[8:10] + "-" + line[5:7] + " " + line[14:22] + " " + line[43:59])

fobj_out.write("20" + line[11:13] + "-" + line[8:10] + "-" + line[5:7] + " " + line[14:22] + " " + line[43:59] n')

fobj_in.close()
fobj_out.close()
fredvonfat
User
Beiträge: 12
Registriert: Mittwoch 12. September 2018, 10:00
Wohnort: Berlin

Mittwoch 12. September 2018, 14:50

Inzwischen sieht mein Script wie folgt aus:

#!/usr/bin/python
#
# Pythonscript
#
# ESI Logfile Auswertung
#
#
#
#
import sys
import string


Datum = sys.argv[1]
#did = sys.argv[2]
err = "ISDN Fehlanruf"
log_path = "/var/log/ESI/ESI2/"
log_file = "F1log_"
log_file2 = "F1spy_"
write_path = "/home/marc/python/"
write_file = "PY_Auswertung.txt"
fobj_in = open(log_path + log_file + Datum + ".txt")
fobj_out = open(write_path + write_file ,"w")
fobj_in2 = open(log_path + log_file2 + Datum + ".txt")

for line in fobj_in:
if err in line:
day = ("20" +line[11:13] + "-" + line[8:10] + "-" + line[5:7])
time = (line[14:22])
time_stamp = (day + " " + time)
duwa = (line[43:47])
numa = (line[48:59])
items = (time_stamp + " " + duwa + " " + numa)
fobj_out.write(items + "\n")


fobj_in.close()
fobj_out.close()
fobj_in2.close()


...und die write Anweisung funktioniert.
jetzt habe ich innerhalb von "for" eine Reihe Variablen definiert, mit denen ich nun das zweite Logfile zeilenweise durchsuchen und bei match die entsprechende Zeile ausgeben möchte.

Wenn ich jetzt eine weitere "for" Anweisung starte, scheint die aber die Variablen nicht mehr zu kennen.

Ich könnte nun die Daten erneut aus dem "write_file" lesen, nur erscheint mir das nicht sehr elegant.
Gibt es eine andere Methode?
Benutzeravatar
__blackjack__
User
Beiträge: 1228
Registriert: Samstag 2. Juni 2018, 10:21

Mittwoch 12. September 2018, 15:42

@fredvonfat: Was heisst wenn Du eine weitere ``for``-Anweisung startest? Die Namen werden in der vorhandenen Schleife in jedem Schleifendurchlauf an den jeweils aktuellen Wert gebunden. Und das ändert sich auch nicht dadurch das man in der ``for``-Schleife eine weitere, verschachtelte Schleife schreibt.

Selbst wenn man *nach* der ersten ``for``-Schleife noch eine weitere schreibt, sind diese Namen an den Wert vom letzten Schleifendurchlauf der ersten Schleife gebunden. Man kann dann natürlich nur mit den letzten Werten etwas machen. Nur wenn die erste ``for``-Schleife keine Fehlerzeile gefunden hat, sind die Namen undefiniert.

Anmerkungen zum Quelltext: `string` wird importiert, aber nirgends verwendet.

Namen werden klein_mit_unterstrichen geschrieben. Ausnahmen: Konstanten (KOMPLETT_GROSS) und Klassen (MixedCase).

Eine Zeichenkette `log_file` zu nennen ist verwirrend. Das ist keine Datei, sondern ein Datei*name*. Wenn man etwas an einen Namen mit `*_file` bindet, dann erwartet der Leser das es sich auch tatsächlich um ein Dateiobjekt handelt. Das was Du kryptisch `fobj_in` nennst, wäre das `log_file`.

Pfadteile setzt man mit `os.path.join()` zusammen. Und auch sonst wird zuviel ``+`` für Zeichenketten verwendet, was man mit Zeichenkettenformatierung mit der `format()`-Methode besser lösen kann.

Dateien sollte man zusammen mit der ``with``-Anweisung öffnen, damit sie auch sicher wieder geschlossen werden.

Bei den Variablenzuweisungen in der Schleife sind unnötige Klammern um die Ausdrücke.

Kryptische Abkürzungen sollte man vermeiden. Bei `err`, `duwa`, und `numa`, muss man raten was das bedeuten mag. Wobei `err` noch am deutlichsten ist, aber meistens für Ausnahmen verwendet wird.

Zwischenstand wäre dann (ungetestet):

Code: Alles auswählen

#!/usr/bin/python
#
# ESI Logfile Auswertung.
#
import os
import sys

ERROR_TEXT = 'ISDN Fehlanruf'
LOG_PATH = '/var/log/ESI/ESI2'
LOG_FILENAME_TEMPLATE = 'F1log_{}.txt'
SPY_FILENAME_TEMPLATE = 'F1spy_{}.txt'
WRITE_PATH = '/home/marc/python'
RESULT_FILENAME = 'PY_Auswertung.txt'


def main():
    datum = sys.argv[1]
    
    log_filename = os.path.join(LOG_PATH, LOG_FILENAME_TEMPLATE.format(datum))
    result_filename = os.path.join(WRITE_PATH, RESULT_FILENAME)
    with open(log_filename) as log_file, open(result_filename, 'w') as out_file:
        for line in log_file:
            if ERROR_TEXT in line:
                day = '20{}-{}-{}'.format(line[11:13], line[8:10], line[5:7])
                time = line[14:22]
                time_stamp = '{} {}'.format(day, time)
                durchwahl = line[43:47]
                number = line[48:59]
                out_file.write(
                    '{} {} {}\n'.format(time_stamp, durchwahl, number)
                )


if __name__ == '__main__':
    main()
“Pets are always a great help in times of stress. And in times of starvation too, o'course.” — Terry Pratchett, Small Gods
fredvonfat
User
Beiträge: 12
Registriert: Mittwoch 12. September 2018, 10:00
Wohnort: Berlin

Donnerstag 13. September 2018, 08:59

Hallo blackjack,

vielen Dank für Deine Korrekturen, da habe ich erst mal ne ganze Reihe an Hausaufgaben zu lösen, um den code im Detail zu verstehen. ;-)
Auf jeden Fall funktioniert er so wie er ist.

Das bisher geschriebene dient zunächst, um Parameter aus einem Logfile zu extrahieren, um dann mit diesen, in einem zweiten Logfile die eigentlichen Informationen zu finden.

Nach dem derzeitigen stand würde ich nun RESULT_FILENAME öffnen und dann die entsprechenden Parameter erneut auslesen.
Macht man das auch so unter Python oder gibt es dafür elegantere Wege?
Benutzeravatar
__blackjack__
User
Beiträge: 1228
Registriert: Samstag 2. Juni 2018, 10:21

Donnerstag 13. September 2018, 09:41

@fredvonfat: Man könnte sich die Informationen aus der ersten Schleife merken, statt sie neu einzulesen.
“Pets are always a great help in times of stress. And in times of starvation too, o'course.” — Terry Pratchett, Small Gods
fredvonfat
User
Beiträge: 12
Registriert: Mittwoch 12. September 2018, 10:00
Wohnort: Berlin

Donnerstag 13. September 2018, 12:44

__blackjack__ hat geschrieben:
Donnerstag 13. September 2018, 09:41
@fredvonfat: Man könnte sich die Informationen aus der ersten Schleife merken, statt sie neu einzulesen.
...und das mache ich mit einer Liste, Matrix, Funktion oder einer Kombi aus denen?
Bin schon fleißig am googeln, finde allerdings noch keine ähnlichen Ansätze. Die in den Tutorials dargestellten Beispiele sind für mich zwar einleuchtent, allerdings fehlt mir noch die Fähigkeit sie auf meinen Fall zu transferieren.

Wenn hier jemand weiterführende Links kennt und sie preisgeben würde, würde mir das sehr helfen.
Benutzeravatar
__blackjack__
User
Beiträge: 1228
Registriert: Samstag 2. Juni 2018, 10:21

Donnerstag 13. September 2018, 13:06

@fredvonfat: Nicht nach ähnlichen Lösungen googlen, sondern einfach mal das Tutorial in der Python-Dokumentation durcharbeiten bis einschliesslich der Grunddatentypen, also bis einschliesslich Kapitel 5 „Data Structures“. Mit denen muss man umgehen können. Ohne Listen, Tupel, Mengen, und Wörterbücher kommt man nicht aus. Auch wenn man in einem Programm nicht alle verwendet, muss man die trotzdem kennen, um entscheiden zu können welche man verwendet.
“Pets are always a great help in times of stress. And in times of starvation too, o'course.” — Terry Pratchett, Small Gods
fredvonfat
User
Beiträge: 12
Registriert: Mittwoch 12. September 2018, 10:00
Wohnort: Berlin

Freitag 14. September 2018, 12:11

Danke für die Anregungen.

Also nach meinen neusten gewonnenen Erkenntnissen, würde ich nun eine liste erzeugen, diese in eine Menge umwandeln (damit die Einträge vereinzelt werden) und dann wieder in eine Liste schreiben.

num_list.append(number)
menge_num_list = set(num_list)
uniq_num_list = list(menge_num_list)

Als nächstes möchte ich jeden Eintrag aus "uniq_num_list" in "spy_file" suchen und alle entsprechenden Zeilen zunächst erstmal ausgeben.

with open(spy_filename) as spy_file, open(result_filename, 'w') as out_file2:
for line in spy_file:
if uniq_num_list in line:
print num

Mein Problem ist nun, "if" erwartet einen String, wie erkläre ich "if" jetzt, dass er die Einträge aus der Liste nacheinander als String verwenden soll?
Benutzeravatar
__blackjack__
User
Beiträge: 1228
Registriert: Samstag 2. Juni 2018, 10:21

Freitag 14. September 2018, 12:49

@fredvonfat: Die Frage ist, ob Du die ganzen Umwandlungen am Anfang tatsächlich brauchst. Die erste auf jeden *nicht* wenn Du gleich mit einem `set()` anfängst und da schon beim Einlesen die Einträge drin speicherst. Und bisher sehe ich noch nicht warum das `set()` danach in eine Liste umgewandelt werden müsste, denn auch bei einem `set()` kann man über die Elemente iterieren.

``if`` erwartet keine Zeichenkette sondern der ``in``-Operator. ``if erwartet einen Wert der als Wahrheitswert verwendet wird.

Wenn man nacheinander etwas für jedes Element aus einer Liste, oder einem anderen iterierbaren Objekt, machen will, braucht man eine Schleife die das gewünschte für jedes Element durchführt. In diesem Fall kann man die dann mit ``break`` abbrechen wenn man einen Treffer gefunden und verarbeitet hat.

Mit der `any()`-Funktion und einem Generatorausdruck bekäme man das auch mit einem Ausdruck nach dem ``if`` hin.

Die Namen sind übrigens wieder nicht so gut gewählt. Grunddatentypen sollten da gar nicht drin vorkommen, denn den konkreten Datentyp ändert man doch ab und zu mal, hier zum Beispiel von `liste` in `set`, und dann müsste man überall die Namen anpassen. Ich würde das einfach `numbers` nennen.
“Pets are always a great help in times of stress. And in times of starvation too, o'course.” — Terry Pratchett, Small Gods
fredvonfat
User
Beiträge: 12
Registriert: Mittwoch 12. September 2018, 10:00
Wohnort: Berlin

Freitag 14. September 2018, 13:42

Oha.
Ja, mit ner while Schleife und der Indizierung der liste experimentiere ich gerade rum, aber "any" Funktion hört sich erst mal spannender an.

Danke dafür.
Benutzeravatar
__blackjack__
User
Beiträge: 1228
Registriert: Samstag 2. Juni 2018, 10:21

Freitag 14. September 2018, 14:38

@fredvonfat: Warum ``while`` und warum Indexzugriffe? Man kann da wie schon gesagt mit einer ``for``-Schleife drüber iterieren, ohne den Umweg über einen Index.
“Pets are always a great help in times of stress. And in times of starvation too, o'course.” — Terry Pratchett, Small Gods
fredvonfat
User
Beiträge: 12
Registriert: Mittwoch 12. September 2018, 10:00
Wohnort: Berlin

Montag 17. September 2018, 12:59

Ok, also

numbers = set()
numbers.add(number)is soweit klar.

Allerdings kann ich mir die Syntax für any() mit "for" noch nicht so ganz vorstellen.

Wie schreibt man in Python den Satz; " Für jeden Eintrag, aus der Menge, "numbers", der in der Datei, "spy_file" vorkommt, drucke mir die ganze Zeile aus."?
Im zweiten Step möchte ich ein bestimmtes Muster aus den Zeilen extrahieren, um dann alle Zeilen, die dieses Muster besitzen in ein neues file zu schreiben.
Der Hintergrund ist der, ich möchte die komplette Kommunikation zweier Geräte in einem File isolieren, über die Telefonnummer komme ich an die Leitungsbezeichnung ran, über die Leitungsbezeichnung komme ich dann an die komplette Kommunikation ran.

Im letzten Step muss noch ein Zeitfilter eingebaut werden, da die Leitungsbezeichnung mehrfach am Tag vorkommen kann und dann die Kommunikation anderer Geräte in dem file auftauchen würden.
fredvonfat
User
Beiträge: 12
Registriert: Mittwoch 12. September 2018, 10:00
Wohnort: Berlin

Dienstag 18. September 2018, 10:20

So, ich bin ein Schritt weiter.
container = set()
with open(spy_filename) as spy_file, open(temp2_filename, 'w') as temp_file:
for line in spy_file:
if any(x in line for x in numbers):
container.add(line)
temp_file.write(line)
fredvonfat
User
Beiträge: 12
Registriert: Mittwoch 12. September 2018, 10:00
Wohnort: Berlin

Dienstag 18. September 2018, 10:55

Jetzt auch mit Tags ;-)

Code: Alles auswählen

    container = set()
    with open(spy_filename) as spy_file, open(temp2_filename, 'w') as temp_file:
        for line in spy_file:
            if any(x in line for x in numbers):
                container.add(line)
                temp_file.write(line)
                
fredvonfat
User
Beiträge: 12
Registriert: Mittwoch 12. September 2018, 10:00
Wohnort: Berlin

Dienstag 18. September 2018, 15:00

So, bis auf den Zeitfilter macht der code erst einmal was er soll, vielleicht hat noch jemand eine Idee, wie man ganze noch etwas zusammenfassen, komprimieren, o.ä. kann.

Code: Alles auswählen

#!/usr/bin/python
#
import os
import sys
import re

ERROR_TEXT = 'ISDN Fehlanruf'
LOG_PATH = '/var/log/ESI/ESI2'
LOG_FILENAME_TEMPLATE = 'F1log_{}.txt'
SPY_FILENAME_TEMPLATE = 'F1spy_{}.txt'
WRITE_PATH = '/home/marc/python'
RESULT_FILENAME = 'PY_Auswertung.txt'
TEMP_FILENAME = 'tempfile.txt'
TEMP2_FILENAME = 'tempfile2.txt'
COM_ID = r".{2},.{4},[0-9]{9,10}"

def main():
    datum = sys.argv[1]
    log_filename = os.path.join(LOG_PATH, LOG_FILENAME_TEMPLATE.format(datum))
    result_filename = os.path.join(WRITE_PATH, RESULT_FILENAME)
    temp_filename = os.path.join(WRITE_PATH, TEMP_FILENAME)
    temp2_filename = os.path.join(WRITE_PATH, TEMP2_FILENAME)
    spy_filename = os.path.join(LOG_PATH, SPY_FILENAME_TEMPLATE.format(datum))
    numbers = set()
    with open(log_filename) as log_file, open(temp_filename, 'w') as out_file:
        for line in log_file:
            if ERROR_TEXT in line:
                day = '20{}-{}-{}'.format(line[11:13], line[8:10], line[5:7])
                time = line[14:22]
                time_stamp = '{} {}'.format(day, time)
                durchwahl = line[43:47]
                number = line[48:59]
                numbers.add(number)
                out_file.write(
                    '{} {} {}\n'.format(time_stamp, durchwahl, number)
                )
    
    container = set()
    container2 = set()
    with open(spy_filename) as spy_file:
        for line in spy_file:
            if any(x in line for x in numbers):
                container.add(line)
        for line in container:
            if re.search(COM_ID,line):
                com_id = line[56:64]
                container2.add(com_id)
    with open(spy_filename) as spy_file, open(result_filename, 'w') as result_file:
        for line in spy_file:
            if any(x in line for x in container2):
                print line
                result_file.write(line)

if __name__ == '__main__':
    main()
[\code]
Antworten