Logfile in eine MSSQL Datenbank einlesen

Installation und Anwendung von Datenbankschnittstellen wie SQLite, PostgreSQL, MariaDB/MySQL, der DB-API 2.0 und sonstigen Datenbanksystemen.
__deets__
User
Beiträge: 14493
Registriert: Mittwoch 14. Oktober 2015, 14:29

stimmt, das ist besser. Wobei insert_entry auch noch umgeschrieben ist ;)
fredvonfat
User
Beiträge: 51
Registriert: Mittwoch 12. September 2018, 10:00
Wohnort: Berlin

@__blackjack__
Stimmt, die ersten beiden Zeilen des Logfiles werden abgeschnitten und dass schon bei der ersten Ausführung. Vielen Dank für den Hinweis.

@__deets__
Erneut vielen Dank für den Schubs.

@Sirius3
Danke für dein "Vorsagen", auf "groupdict" wäre ich wohl noch lange nicht gekommen.

Ihr drei habt mich jetzt richtig angefixed noch tiefer in die Dokus einzutauchen, danke dafür.
fredvonfat
User
Beiträge: 51
Registriert: Mittwoch 12. September 2018, 10:00
Wohnort: Berlin

Ich habe nun den Code etwas erweitert, das Ziel soll sein, ihn mit verschiedenen Argumenten aufzurufen:

1. sys.argv[1] = leer # Hier soll das aktuelle Log-File (auf dem entsprechenden Server) zyklisch (vom scheduler) aufgerufen werden, um die Daten in die DB zu schreiben.
2. sys.argv[1] = Datumsangabe # Hier soll ein spezifisches Logfile auf dem "Archive Server" in die DB geschrieben werden.
3. sys.argv[1] = archive # Hier soll das Archive Verzeichnis aufgerufen werden und alle passenden Files in die DB geschrieben werden.

Punkt 1 und 2 funktionieren schon.
Bei Punkt 3 bekomme ich folgende Fehlemeldung:
Traceback (most recent call last):
File "./python/SQL/test.py", line 105, in <module>
main()
File "./python/SQL/test.py", line 100, in main
process_file(log_filename)
File "./python/SQL/test.py", line 86, in process_file
for entry in parse_log(lines):
File "./python/SQL/test.py", line 61, in parse_log
for line in lines:
File "/usr/lib/python3.5/codecs.py", line 321, in decode
(result, consumed) = self._buffer_decode(data, self.errors, final)
UnicodeDecodeError: 'utf-8' codec can't decode byte 0xdc in position 3939: invalid continuation byte
Was hab ich übersehen bzw. falsch gemacht?
Gibte es eine elegantere Methode, als die, die ich gewählt habe?

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
from datetime import date
import fnmatch

import pymssql

LOG_PATH = '/var/log/ESI/ESI2'
LOG_PATH2 = '/mnt/ESI2/ESI/F1/Log'
LOG_FILENAME_TEMPLATE = os.path.join(LOG_PATH, 'F1log_{}.txt')
LOG_FILENAME_TEMPLATE2 = os.path.join(LOG_PATH2, '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 = 'XXXXXXXXX'
database = 'F1'



def select_file():
    files = []
    for file in os.listdir(LOG_PATH):
        if fnmatch.fnmatch(file, 'F1log_*.txt'):
            files.append(file[6:16])
            data = sorted(files)
    return data
    
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) or ERROR_RE.match(line)
        if match:
            groups = match.groupdict()
            zeit, did, rufnummer = get_common(match)
            message = groups.get('message')
            prom = groups.get('prom')
            site = groups.get('site')
            yield Main(zeit, prom, message, did, rufnummer, site)

def insert_entry(connection, entry):
    with closing(connection.cursor()) as cursor:
            cursor.execute(
                    'INSERT INTO main (zeit, prom, message, did, rufnummer, site)'
                    'VALUES (%s, %s, %s, %s, %s, %s)',
                    entry
                    )
def process_file(log_filename):
    with open (log_filename) as lines:
        connection = pymssql.connect(
                server, user, password, database
                )
        with closing(connection):
            for entry in parse_log(lines):
                try:
                    insert_entry(connection, entry)
                except pymssql.IntegrityError:
                    pass
                except:
                    connection.rollback()
                    raise
                else:
                    connection.commit()

def main():
    if len(sys.argv) >= 2 and sys.argv[1] == 'archive':
        for Date in select_file():
           log_filename = LOG_FILENAME_TEMPLATE.format(Date)
           print(log_filename)
           process_file(log_filename)
           
    elif len(sys.argv) >= 2:
        Date = sys.argv[1]
        log_filename = LOG_FILENAME_TEMPLATE.format(Date)
        print(log_filename)
        process_file(log_filename)

    else:
        Date = date.today()
        log_filename = LOG_FILENAME_TEMPLATE2.format(Date)
        print(log_filename)
        process_file(log_filename)


if __name__ == '__main__':
    main()

Benutzeravatar
__blackjack__
User
Beiträge: 13003
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@fredvonfat: Die Art wie die Argumente von der Kommandozeile verarbeitet werden ist sehr unübersichtlich und in den drei Zweigen wiederholt sich Code. Für die Argumente und Optionen würde man beispielsweise das `argparse`-Modul aus der Standardbibliothek verwenden. Oder etwas externes wie `click`.

Wobei die Unterscheidung zwischen Optionen und Argumenten wichtig ist. Sprich wenn es etwas wie eine „obligatorische Option“ gibt, läuft etwas falsch, denn Optionen sollten, wie der Name schon sagt, optional sein. Die haben also in aller Regel einen Defaultwert.

Offenbar ”rät” Python das UTF-8 ”die” Kodierung ist. Und anscheinend ist das nicht die Kodierung die von der Log-Datei verwendet wird. Deshalb sollte man Textdateien immer explizit mit einer Kodierung öffnen, und zwar der in der die Datei dann auch tatsächlich kodiert ist.

`date` würde man klein schreiben. Das `date` aus `datetime` sollte eigentlich `Date` geschrieben werden. Deswegen benenne ich das immer mit ``as`` beim Importieren um. Alternativ könnte man das jetzige `date` auch `today` nennen.

Der Datentyp `Error` wird nirgends verwendet. Und `Main` könnte man, jetzt wo es die einzige Variante ist, in `Entry` oder `LogEntry` umbenennen.

Namen nummerieren ist schlecht. Entweder will man bessere, passendere Namen verwenden, oder gar keine Einzelnamen, sondern eine Datenstruktur. Im Fall von den Konstanten hier sind es wohl bessere Namen. Oder vielleicht auch gar nicht diese Konstanten, sondern dass das Argumente sind.
“Most people find the concept of programming obvious, but the doing impossible.” — Alan J. Perlis
fredvonfat
User
Beiträge: 51
Registriert: Mittwoch 12. September 2018, 10:00
Wohnort: Berlin

Eine Prüfung der Textdateien mit dem tool, "file", ergab, dass es sich um verschiedene Codierungen handelt.
1. Non-ISO extended-ASCII text, with CRLF line terminators
2. ASCII text, with CRLF line terminators
3. ISO-8859 text, with CRLF line terminators

Hat jemand ne Idee, wie sowas passieren kann?
Wie kann ich das Problem angehen?
Mit "if" und jede Möglichkeit einbeziehen oder gibt es ein tool dass mir automatisch, zur Laufzeit ein einheitliche Kodierung herstellt?
Sirius3
User
Beiträge: 17710
Registriert: Sonntag 21. Oktober 2012, 17:20

@fredvonfat: dass es sich nicht um ASCII handelt, beweißt schonmal die Fehlermeldung, extended-ASCII ist damit alles andere.
Ob es nun ISO-8859 oder irgendeine andere Codierung ist, läßt sich nur raten, weil viele Codierungen nur sehr wenige "ungültige" Zeichen haben. Zudem gibt es noch etliche Varianten von ISO-8859.
Um es nochmal deutlich zu sagen: Du mußt wissen in welcher Kodierung die Log-Datei geschrieben worden ist.

Also: welches Programm schreibt die Log-Dateien? Welche Einstellungen sind dort oder in dem System, auf dem es läuft, hinterlegt?
fredvonfat
User
Beiträge: 51
Registriert: Mittwoch 12. September 2018, 10:00
Wohnort: Berlin

....französische Software, mit ISO8859-15 geht es.....
Antworten