Seite 1 von 4

Re: Logfile in eine MSSQL Datenbank einlesen

Verfasst: Dienstag 11. Juni 2019, 12:56
von Sirius3
@fredvonfat: dann arbeite Dich erst an einfachen Beispielen ins re-Modul ein, dann lernst Du, wie Du es anwenden kannst, und kannst danach Dich auch an kompliziertere Ausdrücke wagen.

Re: Logfile in eine MSSQL Datenbank einlesen

Verfasst: Dienstag 11. Juni 2019, 13:03
von fredvonfat
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.

Re: Logfile in eine MSSQL Datenbank einlesen

Verfasst: Dienstag 11. Juni 2019, 13:27
von Sirius3
daher meine Empfehlung sich in das re-Modul einzuarbeiten.

Re: Logfile in eine MSSQL Datenbank einlesen

Verfasst: Donnerstag 13. Juni 2019, 15:23
von fredvonfat
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
<generator object parse_log at 0x7f70b388be08>
Was hab ich noch nicht verstanden bzw. wo kann ich ansetzen um das zu ändern?

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()

Re: Logfile in eine MSSQL Datenbank einlesen

Verfasst: Donnerstag 13. Juni 2019, 15:34
von __blackjack__
@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.

Re: Logfile in eine MSSQL Datenbank einlesen

Verfasst: Freitag 21. Juni 2019, 10:40
von fredvonfat
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.

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)
        )
        """)

Re: Logfile in eine MSSQL Datenbank einlesen

Verfasst: Freitag 21. Juni 2019, 10:42
von sparrow
Muss man eine Transaktion commiten?

Re: Logfile in eine MSSQL Datenbank einlesen

Verfasst: Freitag 21. Juni 2019, 12:17
von fredvonfat
vielleicht lese ich das auch falsch, allerdings wird hier:
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))')
verwende gibt es auch keine Tabelle, Fehlermeldung und auch im syslog wird kein zugriff auf die Datenbank registriert.

Re: Logfile in eine MSSQL Datenbank einlesen

Verfasst: Freitag 21. Juni 2019, 12:30
von fredvonfat
Mit folgendem Code klappt es auf anhieb.

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))')

Wie kann ich prüfen ob pymssql richtig läuft?

Re: Logfile in eine MSSQL Datenbank einlesen

Verfasst: Freitag 21. Juni 2019, 12:32
von sparrow
Die Dokumentation sieht die Verwendung des Contextmanagers wohl etwas anders vor:

Code: Alles auswählen

with pymssql.connect(server, user, password, "tempdb") as conn:
    with conn.cursor(as_dict=True) as cursor:
        ...
Und in dem von dir verlinkten Beispiel gibt es ein commit.

Re: Logfile in eine MSSQL Datenbank einlesen

Verfasst: Freitag 21. Juni 2019, 12:40
von fredvonfat
Ok, jetzt verstehe ich, was du mit, "commiten", meinst,......hoffentlich. :-)

Re: Logfile in eine MSSQL Datenbank einlesen

Verfasst: Freitag 21. Juni 2019, 12:52
von fredvonfat

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()
so gehts schon mal.

Danke für den Hinweis, Sparrow.

Re: Logfile in eine MSSQL Datenbank einlesen

Verfasst: Montag 24. Juni 2019, 14:48
von fredvonfat
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?

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()

Re: Logfile in eine MSSQL Datenbank einlesen

Verfasst: Montag 24. Juni 2019, 15:04
von __deets__
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?

Re: Logfile in eine MSSQL Datenbank einlesen

Verfasst: Montag 24. Juni 2019, 15:08
von fredvonfat
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?
Wenn exakt die gleiche zeile schon in der Datenbank vorkommt, also jede Zelle der Zeile schon mal eingetragen wurde.

Re: Logfile in eine MSSQL Datenbank einlesen

Verfasst: Montag 24. Juni 2019, 15:11
von __deets__
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.

Re: Logfile in eine MSSQL Datenbank einlesen

Verfasst: Montag 24. Juni 2019, 15:12
von fredvonfat
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?

Re: Logfile in eine MSSQL Datenbank einlesen

Verfasst: Montag 24. Juni 2019, 15:23
von Sirius3
@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.

Re: Logfile in eine MSSQL Datenbank einlesen

Verfasst: Montag 24. Juni 2019, 15:33
von fredvonfat
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.
Ja, daran hatte ich auch schon gedacht.

Würde es reichen, wenn ich "def insert_entries" anpasse? In "def parse_log" benötige ich die Unterscheidung ja noch.
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.
Leider reicht das nicht aus, da der Zeitstempel durchaus gleich sein kann, allerdings ist dann mindestens eines der anderen Felder anders.

Auszuschließen ist, dass Zeit und Rufnummer bei zwei aufeinander folgenden Einträgen gleich ist.

Re: Logfile in eine MSSQL Datenbank einlesen

Verfasst: Montag 24. Juni 2019, 15:35
von __deets__
Dann eben der UNIQUE-constraint ueber die notwendigen Spalten. https://stackoverflow.com/questions/347 ... le-columns