Python Script beendet sich selbst - warum?

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.
mode
User
Beiträge: 10
Registriert: Samstag 4. Oktober 2008, 17:14

Hallo,

ich habe unten stehenden Python Code auf einer SuSE 11 Maschine laufen, es wird zeilenweise von der Seriellen Schnittstelle ttyUSB0 gelesen (ist ein FTDI USB-> Seriell Wandler) und bei besimmten Zeichenketten etwas in die lokale DB geschrieben.
Dies funktioniert auch wunderbar jedoch beendet sich das Script nach einigen Stunden von alleine. Ich habe keine Ahnung warum. In den Systemlogs ist nichts zu finden.


Hier mal der Code:

Code: Alles auswählen

#!/usr/bin/python
import serial
import MySQLdb

conn = MySQLdb.connect (host = "localhost",
                           user = "XXX",
                           passwd = "XXX",
                           db = "TEST")
cursor = conn.cursor()

ser = serial.Serial('/dev/ttyUSB0', 38400, timeout=None)
while 1:
	line = ser.readline()   #read a '\n' terminated line
	if line[0:15] == "1234567890ABCDE":
		cursor.execute("INSERT INTO Main VALUES(NULL, '" + line[18] + "', '" + line[22] + "')")
		
	if line[3:24] == "FFFFFF AAAAA EEEEEEE!":
		cursor.execute("INSERT INTO Main VALUES(NULL, '0', NULL)")

	
cursor.close()
conn.close()
ser.close()
Jemand eine Idee??

Viele Grüße


Mode
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Zunächst einmal solltest Du deinen Query-String anders aufbauen!
[wiki]Parametrisierte SQL-Queries[/wiki]

Was steht denn in der Shell? Wirft das Script eine Exception? Speziell folgendes ist doch recht abenteuerlich:

Code: Alles auswählen

if line[0:15] == "1234567890ABCDE":
was passiert bei kürzeren Strings?

Du könntest auch im Script selber loggen und damit abgefangene Exceptions wegschreiben. So kannst Du auch Fehler finden, wenn das Programm als Backgroundprozess oder daemon ausgeführt word.

Als letztes fiele mir noch die Konfiguration der DB ein. Evtl gibt es eine maximale Dauer von Verbindungen? Wenn dem so ist, sollte das Script immer exakt nach derselben Zeit gestoppt werden. Evtl. solltest Du da mal recherchieren. Ich habe bei DBs meist nur im Zusammenhang mit Webapplikationen gearbeitet, da tritt so ein Problem natürlich nicht auf. Aber denkbar wäre es.
mode
User
Beiträge: 10
Registriert: Samstag 4. Oktober 2008, 17:14

Danke für die Tips, die SQL Querys werde ich anders aufbauen, und das mit dem Zugriff auf Line wenn ein kurzes Wort darin steht scheint in der Tat ein Problem zu sein. Wuerde dies eine Exception werfen die ich behandeln kann?

Ich habe nun erstmal

Code: Alles auswählen

import logging
LOG_FILENAME = '/tmp/py_logging.txt'
logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG,)

logging.debug('This message should go to the log file')
eingefügt um den Fehler einzugrenzen... Das Script laeuft in der tat als Daemon...

Melde mich dann mit den Ergebnissen


VG

Mode
Benutzeravatar
snafu
User
Beiträge: 6731
Registriert: Donnerstag 21. Februar 2008, 17:31
Wohnort: Gelsenkirchen

mode hat geschrieben:das mit dem Zugriff auf Line wenn ein kurzes Wort darin steht scheint in der Tat ein Problem zu sein. Wuerde dies eine Exception werfen die ich behandeln kann?
Hast du schonmal irgendwo gesehen, dass ein if-Statement ne Exception wirft, wenn es nicht zutrifft? Das wäre wohl ein wenig sinnfrei, oder? ;)

EDIT: Achso, du meinst vermutlich ob ein IndexError auftreten würde... Nö, tut er nicht. Man kann aber natürlich prüfen ob der String eine bestimmte Mindestlänge besitzt.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Stimmt! Nen IndexError wird da nicht geworfen. Also ist das nicht die Fehlerquelle.
BlackJack

@mode: So etwas ist aber nun echt schneller ausprobiert, als in einem Forum gefragt:

Code: Alles auswählen

In [226]: line = 'hallo'

In [227]: line[0:100]
Out[227]: 'hallo'

In [228]: line[42]
---------------------------------------------------------------------------
<type 'exceptions.IndexError'>            Traceback (most recent call last)

/home/bj/<ipython console> in <module>()

<type 'exceptions.IndexError'>: string index out of range
Es wäre generell vielleicht eine gute Idee um das ganze Programm eine Ausnahmebehandlung zu machen, und auftretende Ausnahmen zu loggen.
mode
User
Beiträge: 10
Registriert: Samstag 4. Oktober 2008, 17:14

Soo nun lief das Script fast einen Tag lang und ist dann wieder abgeschmiert. Leider wurde in die Log Datei keine Exception geschrieben. Was nun?
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Poste doch mal deinen kompletten Quellcode!

Beschreibe doch mal, wie das Script aufegrufen wird! (rc-script? in einer Shell? etc.)

Hast Du mal meine Theorie mit der Verbindung zur DB verfolgt?
mode
User
Beiträge: 10
Registriert: Samstag 4. Oktober 2008, 17:14

Ok hier das Script:

Code: Alles auswählen

#!/usr/bin/python
import serial
import MySQLdb
import logging
LOG_FILENAME = '/tmp/py_logging.txt'
logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG,)

logging.debug('This message should go to the log file')



conn = MySQLdb.connect (host = "localhost",
                           user = "xx",
                           passwd = "xx",
                           db = "Test")

cursor = conn.cursor()

ser = serial.Serial('/dev/ttyUSB0', 38400, timeout=None)
while 1:
	line = ser.readline()   #read a '\n' terminated line
	if line[0:15] == "Finger erkannt!":
		user=line[18]
		finger=line[22]
		cursor.execute("INSERT INTO Main VALUES(NULL, %s, %s)",  (user,finger))
		
	if line[3:24] == "Finger nicht erkannt!":
		cursor.execute("INSERT INTO Main VALUES(NULL, '0', NULL)")
	
cursor.close()
conn.close()
ser.close()
So in der Log Datei erscheint immer nur die das was ich in Zeile 8 rein schreibe - keine Exception
Bei der Zuweisung von den Variablen User und Finger ist sichergestellt dass Line genug Elemente enthält.

Dass die Datenbank die Verbindung zuruecksetzt habe ich nicht beobachten koennen. Weder in den COnfig noch in den Logfiles des MySQLd konnte ich einen HInweis darauf finden.

Mein Python Script wird durch ein SuSE rc Script gestartet:

Code: Alles auswählen

DESCRIPTION="Test"
EXECUTABLE="/home/mode/fingerreader.py"

# Shell functions sourced from /etc/rc.status:
. /etc/rc.status

# First reset status of this service
rc_reset

case "$1" in
start)
        echo -n "Starting up" $DESCRIPTION
        startproc $EXECUTABLE
        rc_status -v
;;
...
Pekh
User
Beiträge: 482
Registriert: Donnerstag 22. Mai 2008, 09:09

Naja, so ganz von alleine werden die Exceptions nicht ins Log geschrieben. Hier mal ein vielleicht etwas umständliches Beispiel, wie ich das Logging normalerweise handhabe:

Code: Alles auswählen

import sys
import time
import logging
import traceback

def configure_logging():        
    logging.basicConfig(level=logging.DEBUG,
       format='%(levelname)-8s %(message)s',
       filename='kassenlog',
       filemode='a')
    logging.info("====== Programm gestartet am %s ======" %(time.ctime()))
    # Fehler sollen nicht nur im Log, sondern auch auf der Konsole landen:
    console = logging.StreamHandler()
    console.setLevel(logging.WARNING)
    formatter = logging.Formatter('%(message)s')
    console.setFormatter(formatter)
    logging.getLogger("").addHandler(console)

def hook(t,v,tb):
    l = traceback.format_exception(t,v,tb)
    s = ''.join(l)
    logging.exception(s)

if __name__ == "__main__":
    configure_logging()    
    sys.excepthook = hook
Statt des hooks kannst du auch ein try/except um deinen ganzen Code machen und dann im Except-Block logging.exception verwenden.
mode
User
Beiträge: 10
Registriert: Samstag 4. Oktober 2008, 17:14

Ok das wusste ich nicht.
Also ein Try "Block" um das while 1 und darunter

Code: Alles auswählen

except:
	 logging.exception
Lasse ich über Nacht mal laufen....
Pekh
User
Beiträge: 482
Registriert: Donnerstag 22. Mai 2008, 09:09

Jein. 'logging.exception()' ist eine Funktion, der du natürlich noch den zu loggenden Text übergeben mußt. Das kannst du mit eigenen Texten tun, dann mußt du aber eine ganze Reihe von denkbaren Exceptions abfangen und jeweils spezialisierte Fehlertexte schreiben lassen. Einfach nur zu schreiben: "Ein Fehler ist aufgetreten" dürfte dir nicht weiterhelfen.
Oder du nutzt wie ich in meinem Beispiel oben das Traceback-Modul.
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Vor allem solltest Du das Script einmal im Trockenlauf testen, d.h. mal per raise gezielt eine Exception in der Schleife auslösen. So brauchst Du nicht so lange zu warten, bis das Script mal wieder nach x Stunden abbricht ;-)

Außerdem würde ich ggf. mal die Queries loggen oder aber auch die Inputs der seriellen Schnittstelle. Vor allem siehst Du dann gut, ob es vor dem Crash Aktivitäten gegeben hat.
Benutzeravatar
Sr4l
User
Beiträge: 1091
Registriert: Donnerstag 28. Dezember 2006, 20:02
Wohnort: Kassel
Kontaktdaten:

Wenn es immer genau 12Stunden sind liegt es am MySQL Connection Timeout. ;-)
MySQL Connections werden, auch wenn sie noch benutzt werden nach 12Std, bzw. das was die Config sagt getrennt.
Default sollte 12Std sein und man sollte das auch nicht hochsetzen, falls das überhaupt möglich ist?!?

Du beseitigst das Problem indem du ein `try` für `cursor.execute` machst wenn das fehlschlägt baust du die Verbindung neu auf und machst noch mal den `cursor.execute` in die DB.
mode
User
Beiträge: 10
Registriert: Samstag 4. Oktober 2008, 17:14

Habe nun in meine except

Code: Alles auswählen

except:
	 logging.exception (traceback.format_exc())

geschrieben - somit werde exceptions auch ins Log geschrieben.
Dein Code von Pekh habe ich nicht so ganz verstanden - daher habe mich fuer die minimalistische Version entschieden.

Des weiteren wird jetzt die DB Verbindung erst geoeffnet, wenn ein guelter String von der seriellen Schnittstelle gelesen wurde und danach direkt wieder geschlossen. Somit wird das Datenbanktimeoutproblem - wenn es das denn war umgangen.

Ich melde mich in einigen Tagen mit dem Resultat.


Viele Gruesse


Mode
Pekh
User
Beiträge: 482
Registriert: Donnerstag 22. Mai 2008, 09:09

Dann würde ich dir mal empfehlen, die Dokumentation zum Traceback-Modul zu lesen. Da steht dann zum Beispiel drin, daß 'traceback.format_exception' drei Argumente erwartet und eine Liste zurückliefert. Was man mit minimaler Anstrengung auch aus meinem obigen Quellcode entnehmen könnte :roll:

Im Übrigen solltest du die Empfehlung von Hyperion, einfach mal ein 'raise Exception' in dein Programm zu schreiben, unbedingt befolgen. So kannst du den Logging-Mechanismus testen, ohne stundenlang warten zu müssen. Und dann wäre dir auch aufgefallen, daß das so nicht funktioniert.

Noch was: Die Verbindung rund um jeden Schreibvorgang aufzubauen und wieder zu schließen ist eine gewaltige Verschwendung von Ressourcen und dürfte dein Programm um ein Vielfaches verlangsamen. Ich gehe jedenfalls davon aus, daß da relativ oft etwas über die Schnittstelle kommt.
lunar

@Pekh
Wenn du anderen Leuten empfiehlst, die Doku zu lesen, stünde es dir gut an, gleiches zu tun. Dein Beispiel offenbar nämlich ebenfalls, dass du die Doku keinesfalls vollständig gelesen hast. Insofern kann man sogar froh sein, dass der OP nicht deinen obigen Quellcode übernommen hat, welcher zwar vielleicht funktioniert, aber auch nicht gerade ein Musterbeispiel für die Verwendung des logging-Moduls ist.

Hättest du die Doku zum logging Modul nämlich gelesen, dann wüsstest du, dass "logging.exception()" nichts anderes tut, als "logging.error()" mit Informationen über die Ausnahme aufzurufen. In der Doku steht sogar explizit "Call this method only from an exception handler!".

Für den Fall, dass nicht der Traceback der aktuellen Ausnahme gewünscht ist, gibt es im übrigen das Keyword-Argument "exc_info" für ".error()", welches man mit einem Tupel mit Informationen über die Ausnahme belegen kann (so wie es z.B. von sys.exc_info()" zurückgegeben wird.
mode
User
Beiträge: 10
Registriert: Samstag 4. Oktober 2008, 17:14

@Pekh
Mit minimaler Anstrengung kann man herausfinden, dass ich traceback.format_exc verwendet habe was nur einen optionalen Parameter erwartet. ;-)
Siehe: http://openbook.galileo-press.de/python ... 21_006.htm

Das mit der Raise Exception habe ich gemacht und es hat auch funktioniert (Stand im Logfile).

Es kommen etwa 10 Datensätze pro 24 Stunden in die DB. Von daher denke ich der "Overhead" ist zu vernachlässigen und weniger schlimm als eine Verbindung die immer offen ist und dann oft noch wieder aufgebaut werden muss weil sie in ein TImeout gelaufen ist...

VG

Mode
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

mode hat geschrieben:@Pekh
Mit minimaler Anstrengung kann man herausfinden, dass ich traceback.format_exc verwendet habe was nur einen optionalen Parameter erwartet. ;-)
Siehe: http://openbook.galileo-press.de/python ... 21_006.htm
Wobei ich mich eher auf die Doku beziehen würde ;-) (Nebenbei ist das Buch hier nicht gerne gesehen - es scheint viele Fehler zu haben und zu vermitteln)
Es kommen etwa 10 Datensätze pro 24 Stunden in die DB. Von daher denke ich der "Overhead" ist zu vernachlässigen und weniger schlimm als eine Verbindung die immer offen ist und dann oft noch wieder aufgebaut werden muss weil sie in ein TImeout gelaufen ist...
Das stimmt dann wohl!
Pekh
User
Beiträge: 482
Registriert: Donnerstag 22. Mai 2008, 09:09

Ich habe mich aus einer (schlechten) Laune heraus dazu hinreißen lassen, in einem nicht angemessenen Tonfall Dinge zu schreiben, die so nicht in Ordnung sind. Das tut mir leid und ich möchte mich dafür entschuldigen.

Zu Lunars Antwort möchte ich noch ein paar Dinge anmerken / fragen:

1) Habe ich nicht behauptet, daß mein Beispiel das Musterbeispiel für die Verwendung des Logging-Moduls ist (aber ich wäre sehr dankbar, wenn du eines bereitstellen könntest). So wie ich es oben gepostet habe ist es bei mir im Einsatz, und ich hatte bislang keinen Grund zu klagen. Die Existenz von sys.exc_info war mir nicht bewußt, aber ich werde es bei zukünftigen Entwicklungen berücksichtigen.

2) Noch eine Verständnisfrage: Was bitte ist meine Funktion 'hook', wenn nicht ein Exceptionhandler?
Antworten