Logfile in eine MSSQL Datenbank einlesen

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
Sirius3
User
Beiträge: 9893
Registriert: Sonntag 21. Oktober 2012, 17:20

Dienstag 11. Juni 2019, 12:56

@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.
fredvonfat
User
Beiträge: 37
Registriert: Mittwoch 12. September 2018, 10:00
Wohnort: Berlin

Dienstag 11. Juni 2019, 13:03

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.
Sirius3
User
Beiträge: 9893
Registriert: Sonntag 21. Oktober 2012, 17:20

Dienstag 11. Juni 2019, 13:27

daher meine Empfehlung sich in das re-Modul einzuarbeiten.
fredvonfat
User
Beiträge: 37
Registriert: Mittwoch 12. September 2018, 10:00
Wohnort: Berlin

Donnerstag 13. Juni 2019, 15:23

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()
Benutzeravatar
__blackjack__
User
Beiträge: 3350
Registriert: Samstag 2. Juni 2018, 10:21

Donnerstag 13. Juni 2019, 15:34

@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.
“I am Dyslexic of Borg, Your Ass will be Laminated” -- unknown
fredvonfat
User
Beiträge: 37
Registriert: Mittwoch 12. September 2018, 10:00
Wohnort: Berlin

Freitag 21. Juni 2019, 10:40

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)
        )
        """)
Benutzeravatar
sparrow
User
Beiträge: 1166
Registriert: Freitag 17. April 2009, 10:28

Freitag 21. Juni 2019, 10:42

Muss man eine Transaktion commiten?
fredvonfat
User
Beiträge: 37
Registriert: Mittwoch 12. September 2018, 10:00
Wohnort: Berlin

Freitag 21. Juni 2019, 12:17

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.
fredvonfat
User
Beiträge: 37
Registriert: Mittwoch 12. September 2018, 10:00
Wohnort: Berlin

Freitag 21. Juni 2019, 12:30

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?
Benutzeravatar
sparrow
User
Beiträge: 1166
Registriert: Freitag 17. April 2009, 10:28

Freitag 21. Juni 2019, 12:32

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.
fredvonfat
User
Beiträge: 37
Registriert: Mittwoch 12. September 2018, 10:00
Wohnort: Berlin

Freitag 21. Juni 2019, 12:40

Ok, jetzt verstehe ich, was du mit, "commiten", meinst,......hoffentlich. :-)
fredvonfat
User
Beiträge: 37
Registriert: Mittwoch 12. September 2018, 10:00
Wohnort: Berlin

Freitag 21. Juni 2019, 12:52

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.
fredvonfat
User
Beiträge: 37
Registriert: Mittwoch 12. September 2018, 10:00
Wohnort: Berlin

Montag 24. Juni 2019, 14:48

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()
__deets__
User
Beiträge: 5600
Registriert: Mittwoch 14. Oktober 2015, 14:29

Montag 24. Juni 2019, 15:04

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?
fredvonfat
User
Beiträge: 37
Registriert: Mittwoch 12. September 2018, 10:00
Wohnort: Berlin

Montag 24. Juni 2019, 15:08

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.
Antworten