Logfile in eine MSSQL Datenbank einlesen
-
- User
- Beiträge: 51
- Registriert: Mittwoch 12. September 2018, 10:00
- Wohnort: Berlin
Na ja, mein zu letzt geposteter Schnipsel sehe ich jetzt nicht als so kompliziert an, es ist auch nicht so, dass die regex mir völlig fremd sind, nur unter Python hatte ich damit noch nichts zu tun.
-
- User
- Beiträge: 51
- Registriert: Mittwoch 12. September 2018, 10:00
- Wohnort: Berlin
Ich bin nun schon ein kleines Stück weiter gekommen, dank eurer Hilfe habe ich auch die DB-Struktur noch einmal überdacht.
Eine Tabelle sollte reichen und die exclusionen habe ich auch rausgenommen, dass ist dann ein Job für die Datenbankabfrage.
Zunächst denke ich, dass es besser ist erst einmal alles mitzunehmen.
Auf dem Weg, den Code zu lernen bin ich nun bei den Funktionen bzw. deren Verkettungen angelangt.
Bevor ich mich an die Datenbank mache würde ich mir die Daten gerne ausgeben lassen.
Allerdings bekomme ich immer ein
Eine Tabelle sollte reichen und die exclusionen habe ich auch rausgenommen, dass ist dann ein Job für die Datenbankabfrage.
Zunächst denke ich, dass es besser ist erst einmal alles mitzunehmen.
Auf dem Weg, den Code zu lernen bin ich nun bei den Funktionen bzw. deren Verkettungen angelangt.
Bevor ich mich an die Datenbank mache würde ich mir die Daten gerne ausgeben lassen.
Allerdings bekomme ich immer ein
Was hab ich noch nicht verstanden bzw. wo kann ich ansetzen um das zu ändern?<generator object parse_log at 0x7f70b388be08>
Code: Alles auswählen
#!/usr/bin/env python3
#
#
#
#
#
#
import os
import sys
import re
from collections import namedtuple
from contextlib import closing
from datetime import datetime as DateTime
import pymssql
LOG_PATH = '/var/log/ESI/ESI2'
LOG_FILENAME_TEMPLATE = os.path.join(LOG_PATH, 'F1log_{}.txt')
COMMON_PATTERN = r'^(\d+) (?P<zeit>\d{2}/\d{2}/\d{2} \d{2}:\d{2}:\d{2}) '
MAIN_RE = re.compile(COMMON_PATTERN + r'(?P<prom>\d{8}) (?P<message>.{14}) .+ N00 (?P<did>\d{4}) (?P<rufnummer>\d+)? ?#')
ERROR_RE = re.compile(COMMON_PATTERN + r'ISDN Fehlanruf C.00 (?P<did>\d+) (?P<rufnummer>\d+)? ?Diag:(?P<message>.{14})')
Main = namedtuple('Main', 'zeit prom message did rufnummer')
Error = namedtuple('Error', 'zeit prom message did rufnummer')
def get_common(match):
zeit = DateTime.strptime(match.group('zeit'), '%d/%m/%y %H:%M:%S')
did = int(match.group('did'))
rufnummer = match.group('rufnummer')
return (zeit, did, rufnummer)
def parse_log(lines):
for line in lines:
match = MAIN_RE.match(line)
if match:
zeit, did, rufnummer = get_common(match)
message = match.group('message')
prom = match.group('prom')
yield Main(zeit, prom, message, did, rufnummer)
else:
match = ERROR_RE.match(line)
if match:
zeit, did, rufnummer = get_common(match)
prom = 'x{8}'
message = match.group('message')
yield Error(zeit, prom, message, did, rufnummer)
def main():
log_filename = LOG_FILENAME_TEMPLATE.format(sys.argv[1])
with open (log_filename) as lines:
print(parse_log(lines))
main()
- __blackjack__
- User
- Beiträge: 13100
- Registriert: Samstag 2. Juni 2018, 10:21
- Wohnort: 127.0.0.1
- Kontaktdaten:
@fredvonfat: `parse_log()` ist eine Generatorfunktion (wegen ``yield``) — die macht beim Aufruf nichts ausser sofort ein Generator-Objekt zu liefern. Die Arbeit passiert erst wenn man die Elemente von diesem Generator abfragt. Also entweder schreibst Du da eine Schleife drüber und gibts die einzelnen Elemente aus, oder Du rufst `list()` mit dem Generator auf, damit die Elemente alle in einer Liste gesammelt werden, und gibst dann die Liste aus.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
-
- User
- Beiträge: 51
- Registriert: Mittwoch 12. September 2018, 10:00
- Wohnort: Berlin
Das mit dem 'list()', war ein sehr guter tip. Die testweise Ausgabe funktioniert jetzt.
Allerdings habe ich gerade ein Problem mit pymssql.
folgender Code liefert keine Fehlermeldung, legt aber auch keine Tabelle an.
Allerdings habe ich gerade ein Problem mit pymssql.
folgender Code liefert keine Fehlermeldung, legt aber auch keine Tabelle an.
Code: Alles auswählen
#!/usr/bin/env python3
import os
import re
import sys
import pymssql
from contextlib import closing
conn = pymssql.connect(
server='localhost',
user='sa',
password='xxx',
database='F1'
)
with closing(conn.cursor()) as cursor:
cursor.execute("""
CREATE TABLE main(
zeit DATETIME,
prom VARCHAR(8),
message VARCHAR(14),
did INTEGER,
rufnummer VARCHAR(12),
site VARCHAR(10)
)
""")
-
- User
- Beiträge: 51
- Registriert: Mittwoch 12. September 2018, 10:00
- Wohnort: Berlin
vielleicht lese ich das auch falsch, allerdings wird hier:
Wenn ich
verwende gibt es auch keine Tabelle, Fehlermeldung und auch im syslog wird kein zugriff auf die Datenbank registriert.
sowas ähnliches gemacht, Transaktion in doppel quotes gesetzt.
Wenn ich
Code: Alles auswählen
with closing(conn.cursor()) as cursor:
cursor.execute(
'CREATE TABLE main(zeit DATETIME, prom VARCHAR(8), message VARCHAR(14), did INTEGER, rufnummer VARCHAR(12), site VARCHAR(10))')
-
- User
- Beiträge: 51
- Registriert: Mittwoch 12. September 2018, 10:00
- Wohnort: Berlin
Mit folgendem Code klappt es auf anhieb.
Wie kann ich prüfen ob pymssql richtig läuft?
Code: Alles auswählen
conn = _mssql.connect(
server='localhost',
user='sa',
password='xxx',
database='F1'
)
conn.execute_non_query('CREATE TABLE main(zeit DATETIME, prom VARCHAR(8), message VARCHAR(14), did INTEGER, rufnummer VARCHAR(12), site VARCHAR(10))')
Die Dokumentation sieht die Verwendung des Contextmanagers wohl etwas anders vor:
Und in dem von dir verlinkten Beispiel gibt es ein commit.
Code: Alles auswählen
with pymssql.connect(server, user, password, "tempdb") as conn:
with conn.cursor(as_dict=True) as cursor:
...
-
- User
- Beiträge: 51
- Registriert: Mittwoch 12. September 2018, 10:00
- Wohnort: Berlin
Ok, jetzt verstehe ich, was du mit, "commiten", meinst,......hoffentlich.
-
- User
- Beiträge: 51
- Registriert: Mittwoch 12. September 2018, 10:00
- Wohnort: Berlin
Code: Alles auswählen
cursor = conn.cursor()
cursor.execute('CREATE TABLE main(zeit DATETIME, prom VARCHAR(8), message VARCHAR(14), did INTEGER, rufnummer VARCHAR(12), site VARCHAR(10))')
conn.commit()
conn.close()
Danke für den Hinweis, Sparrow.
-
- User
- Beiträge: 51
- Registriert: Mittwoch 12. September 2018, 10:00
- Wohnort: Berlin
Aktuell ist mein Zwischenstand, dass alle relevanten Einträge in die Tabelle eingetragen werden und leider bei jeder Ausführung erneut.
Das soll doch durch "try and except" und "isinstance()" verhindert werden, oder?
Komme aktuell durch suchen nicht weiter.
Von meinem denken her, würde ich mit einer Abfrage die Zeilen der Tabelle in Strings einlesen und sie dann mit den match lines vergleichen.
Da allerdings täglich ca. 5 bis 8 tausend Einträge in die Datenbank kommen werden, wird das auf dauer sicherlich sehr Hardware intensiv.
Welche Möglichkeiten gibt es noch?
Kann mir das bitte nochmal jemand genauer erläurtern?
Das soll doch durch "try and except" und "isinstance()" verhindert werden, oder?
Komme aktuell durch suchen nicht weiter.
Von meinem denken her, würde ich mit einer Abfrage die Zeilen der Tabelle in Strings einlesen und sie dann mit den match lines vergleichen.
Da allerdings täglich ca. 5 bis 8 tausend Einträge in die Datenbank kommen werden, wird das auf dauer sicherlich sehr Hardware intensiv.
Welche Möglichkeiten gibt es noch?
Kann mir das bitte nochmal jemand genauer erläurtern?
Code: Alles auswählen
#!/usr/bin/env python3
#
import os
import sys
import re
from collections import namedtuple
from contextlib import closing
from datetime import datetime as DateTime
import pymssql
LOG_PATH = '/var/log/ESI/ESI2'
LOG_FILENAME_TEMPLATE = os.path.join(LOG_PATH, 'F1log_{}.txt')
COMMON_PATTERN = r'^(\d+) (?P<zeit>\d{2}/\d{2}/\d{2} \d{2}:\d{2}:\d{2}) '
MAIN_RE = re.compile(COMMON_PATTERN + r'(?P<prom>\d{8}) (?P<message>.{14}) .+ N00 (?P<did>\d{4}) (?P<rufnummer>\d{8,12})? .+ ?SITE=(?P<site>\d{8,10})')
ERROR_RE = re.compile(COMMON_PATTERN + r'ISDN Fehlanruf C.00 (?P<did>\d{4}) (?P<rufnummer>\d{8,12})? ?Diag:(?P<message>[A-Z ]{14,18})')
Main = namedtuple('Main', 'zeit prom message did rufnummer site')
Error = namedtuple('Error', 'zeit prom message did rufnummer site')
server = 'localhost'
user = 'sa'
password = 'xxx'
database = 'F1'
def create_tables(connection):
with closing(connection.cursor()) as cursor:
cursor.execute(
'CREATE TABLE main(zeit DATETIME, prom VARCHAR(8),'
'message VARCHAR(18), did INTEGER, rufnummer VARCHAR(12)'
'site VARCHAR(10))'
)
def get_common(match):
zeit = DateTime.strptime(match.group('zeit'), '%d/%m/%y %H:%M:%S')
did = int(match.group('did'))
rufnummer = match.group('rufnummer')
return (zeit, did, rufnummer)
def parse_log(lines):
for line in lines:
match = MAIN_RE.match(line)
if match:
zeit, did, rufnummer = get_common(match)
message = match.group('message')
prom = match.group('prom')
site = match.group('site')
yield Main(zeit, prom, message, did, rufnummer, site)
else:
match = ERROR_RE.match(line)
if match:
zeit, did, rufnummer = get_common(match)
prom = None
site = None
message = match.group('message')
yield Error(zeit, prom, message, did, rufnummer, site)
def insert_entries(connection, entries):
with closing(connection.cursor()) as cursor:
for entry in entries:
if isinstance(entry, Main):
cursor.execute(
'INSERT INTO main (zeit, prom, message, did, rufnummer, site)'
'VALUES (%s, %s, %s, %s, %s, %s)',
entry
)
elif isinstance(entry, Error):
cursor.execute(
'INSERT INTO main (zeit, prom, message, did, rufnummer, site)'
'VALUES (%s, %s, %s, %s, %s, %s)',
entry
)
def main():
log_filename = LOG_FILENAME_TEMPLATE.format(sys.argv[1])
with open (log_filename) as lines:
connection = pymssql.connect(
server, user, password, database
)
with closing(connection):
try:
insert_entries(connection, parse_log(lines))
except:
connection.rollback()
raise
else:
connection.commit()
main()
Warum sollte isinstance das verhindern? isinstance dient der Unterscheidung in welcher Tabelle ein Eintrag gemacht wird. Da isinstance aber keine Eigenschaften der Objekte beruecksichtigt jenseits ihrer Klasse, verhindert es auch keine Mehrfachanlage. Und warum sollte das try/except daran etwas aendern? Das wuerde - wenn es greift - den *GESAMTEN* Prozess des eintragens von Daten rueckgaengig machen. Ebenfalls nicht pro Eintrag irgendetwas pruefen.
Deine Tabellen enthalten keine Kriterien, nach denen die Eindeutigkeit eines Eintrags bestimmt wird. Also meckert die Datenbank auch nicht, wenn es zu einem Doppeleintrag kommt. Wodurch bestimmt sich fuer dich, ob ein Eintrag schonmal vorgekommen ist?
Deine Tabellen enthalten keine Kriterien, nach denen die Eindeutigkeit eines Eintrags bestimmt wird. Also meckert die Datenbank auch nicht, wenn es zu einem Doppeleintrag kommt. Wodurch bestimmt sich fuer dich, ob ein Eintrag schonmal vorgekommen ist?
-
- User
- Beiträge: 51
- Registriert: Mittwoch 12. September 2018, 10:00
- Wohnort: Berlin
Wenn exakt die gleiche zeile schon in der Datenbank vorkommt, also jede Zelle der Zeile schon mal eingetragen wurde.Deine Tabellen enthalten keine Kriterien, nach denen die Eindeutigkeit eines Eintrags bestimmt wird. Also meckert die Datenbank auch nicht, wenn es zu einem Doppeleintrag kommt. Wodurch bestimmt sich fuer dich, ob ein Eintrag schonmal vorgekommen ist?
Dann musst du das so definieren, indem du einen UNIQUE-Constraint einfuehrst, der alle Spalten umfasst. Danach solltest du zumindest einen Fehler bekommen, wenn du versuchst, die gleiche Zeile zweimal anzulegen. Und dann geht's weiter, du musst dein Skript so umschreiben, dass es entweder alles zeilenweise einfueght, committet oder zurueckrollt, oder du versuchst dich an Mechanismen wie sie hier https://stackoverflow.com/questions/209 ... -not-exist beschrieben werden.
-
- User
- Beiträge: 51
- Registriert: Mittwoch 12. September 2018, 10:00
- Wohnort: Berlin
Also müsste man jeder Zeile das Attribut, "unique" geben, damit es eine Fehlermeldung gibt, auf grund derer man dann die eintragung dieser Zeile skippen kann?
@fredvonfat: nein. Du mußt alle Felder zusammen einen UNIQUE-Constraint geben, weil nur alle Felder zusammen eindeutig sein müssen.
Inzwischen hast Du es ja geschafft, dass Fehlerzeilen und normale Zeilen das gleiche Schema teilen, dann ist aber eine Unterscheidung zwischen Error und Main unsinnig.
Wenn man garantieren kann, dass nachträglich keine weiteren Einträge hinzukommen, wäre das einfachste Kriterium das Datum, so dass man alle Einträge skippen kann, die älter sind als der neuste Eintrag in der Datenbank.
Inzwischen hast Du es ja geschafft, dass Fehlerzeilen und normale Zeilen das gleiche Schema teilen, dann ist aber eine Unterscheidung zwischen Error und Main unsinnig.
Wenn man garantieren kann, dass nachträglich keine weiteren Einträge hinzukommen, wäre das einfachste Kriterium das Datum, so dass man alle Einträge skippen kann, die älter sind als der neuste Eintrag in der Datenbank.
-
- User
- Beiträge: 51
- Registriert: Mittwoch 12. September 2018, 10:00
- Wohnort: Berlin
Ja, daran hatte ich auch schon gedacht.nzwischen hast Du es ja geschafft, dass Fehlerzeilen und normale Zeilen das gleiche Schema teilen, dann ist aber eine Unterscheidung zwischen Error und Main unsinnig.
Würde es reichen, wenn ich "def insert_entries" anpasse? In "def parse_log" benötige ich die Unterscheidung ja noch.
Leider reicht das nicht aus, da der Zeitstempel durchaus gleich sein kann, allerdings ist dann mindestens eines der anderen Felder anders.Wenn man garantieren kann, dass nachträglich keine weiteren Einträge hinzukommen, wäre das einfachste Kriterium das Datum, so dass man alle Einträge skippen kann, die älter sind als der neuste Eintrag in der Datenbank.
Auszuschließen ist, dass Zeit und Rufnummer bei zwei aufeinander folgenden Einträgen gleich ist.
Dann eben der UNIQUE-constraint ueber die notwendigen Spalten. https://stackoverflow.com/questions/347 ... le-columns