Seite 1 von 2
Um jeden Preis protokollieren?
Verfasst: Sonntag 30. Oktober 2016, 13:25
von Sophus
Hallo Leute,
wie die Überschrift dieses Themas erahnen lässt, geht es um das Logging-Modul. Allerdings geht es mir nicht darum, wie man das Modul installiert oder benutzt. Ich bin sozusagen über das Grundgerüst hinaus und möchte das Protokollieren "richtig" anwenden. Ich habe dazu versucht einige Projekte zu finden, nur um zu sehen, wie das Protokollieren praktiziert wird. Damit ihr wisst, wovon ich rede, habe ich mal eine Beispiel mitgebracht - es ist NICHT ausführbar. In diesem Schnipsel seht ihr, dass gleich zum Anfang der Funktion protokolliert wird, dass die Funktion aufgerufen wird. Ganz am Ende der Funktion wird protokolliert, dass die Funktion erfolgreich aufgerufen wurde. Und dazwischen sehen wir, dass die QLineEdit()-Objekte durch die setText()-Methode gefüllt wird. Und hier sehen wir, dass ich ebenfalls protokolliere - und das gleich zwei mal. Erst steht, dass die QLineEdit gefüllt wird, und danach, dass das Füllen erfolgreich war. Und das wiederhole ich solange, bis alle QLineEdits durch sind.
Meine Frage nun, ist das der beste Weg zu Protokollieren? Oder sollte man so wenig wie möglich protokollieren? Denn Wie wir anhand dieses Beispiels sehen, bläht sich der Quelltext richtig auf. Und wie ihr seht, habe ich die Inhalte noch gar nicht "mitgeschnitten". Auch das ist die nächste Frage. Alles inhaltlich mitschneiden? Aber dann wird es richtig heikle. Da sich mein Programm auch unter anderem mit der MySQL-Datenbank verbinden kann, frage ich mich dann, sollen die Login-Daten mitgeschnitten werden? Zum Beispiel wäre es ja wichtig, was der User alles eingegeben hat, wenn beim Login Probleme auftauchen. Oder sollten einem die Inhalte gar nicht interessieren?
Mir geht es im Kern darum, ein Gespür zu bekommen, was man protokolliert und was man beiseite lässt. Ich denke mal, dass ich in meinem Beispiel stark "überzogen" habe, richtig? Wahrscheinlich hätte es wohl gereicht, einmal zu erwähnen, dass nun an besagter Stelle die QLineEdit()-Objekte gefüllt werden.
Wie gesagt, ich habe versucht einige Projekte zu finden, um die Handschrift des Protokollierens zu erlernen. Um zu sehen, was alles mitgeschnitten wird, und was der Entwickler getrost raus lässt. Wenn ihr Projekte kennt, bei denen das Logging wunderbar gezeigt wird, dann immer her damit.
Damit kein Missverständnis entsteht. Mir geht es nicht ums "Spionieren". Mir geht es darum, wenn es Probleme gibt, dass ich das anhand der Log-Datei alles haargenau nachvollziehen kann.
Code: Alles auswählen
def load_and_show_settings(self):
'''
NOTICE:
=======
Once the user loads this setting window, we want to show him all his saved settings.
PARAMETERS:
===========
:return - Nothing is returned. The statement 'return'
terminates a function. That makes sure that the
function is definitely finished.
'''
self.custom_logger.info("="*10 +" START FUNCTION (" + inspect.stack()[0][
3] + ") - (" + FILE_NAME + ")" + "="*10)
self.custom_logger.info("Calling the function (" + inspect.stack()[0][3] + ") - (" + FILE_NAME + ")")
'''
In the setText()-method we access to a dictionary at first,
because the loaded settings are saved in the dictionary.
We use the key to get the value, that is saved behind
the key. With the obtained value we fill it in the QLineEdit()-object.
'''
self.custom_logger.info("Setting the text of lineEdit_url_update()-object - (" + __name__ + ")")
self.ui_setting.lineEdit_url_update.setText('Here your text')
self.custom_logger.info("The text of lineEdit_url_update()-object is set successfully - (" + __name__ + ")")
self.custom_logger.info("Setting the text of lineEdit_temp_folder()-object - (" + __name__ + ")")
self.ui_setting.lineEdit_temp_folder.setText('Here your text')
self.custom_logger.info("The text of lineEdit_temp_folder()-object is set successfully - (" + __name__ + ")")
self.custom_logger.info("Setting the text of lineEdit_server_version_address()-object - (" + __name__ + ")")
self.ui_setting.lineEdit_server_version_address.setText('Here your text')
self.custom_logger.info("The text of lineEdit_server_version_address()-object is set successfully - (" + __name__ + ")")
self.custom_logger.info("Setting the text of lineEdit_timeout_update()-object - (" + __name__ + ")")
self.ui_setting.lineEdit_timeout_update.setText('Here your text')
self.custom_logger.info("The text of lineEdit_timeout_update()-object is set successfully - (" + __name__ + ")")
self.custom_logger.info("The function (" + inspect.stack()[0][3] + ") is called successfully - (" + FILE_NAME + ")")
self.custom_logger.info("="*10 + " END FUNCTION (" + inspect.stack()[0][3] + ") - (" + FILE_NAME + ")" + "="*10)
return
Re: Um jeden Preis protokollieren?
Verfasst: Sonntag 30. Oktober 2016, 14:09
von Sirius3
@Sophus: Log-Daten sollen den Programmfluß nachvollziehbar machen. Wenn ich schon weiß, dass die Funktion gestartet wurde, dann weiß sie aufgerufen wurde und dass danach ein setText kommt. Diese drei Zeilen werden also immer so in der Logdatei stehen und bieten keinen Mehrwert. Funktionsname, Datei, Zeilennummer usw. kann man automatisch protokollieren; Dein ganzen händisches Stringgefummel ist also unnötig. Sollte es doch mal nötig sein, z.B. Aufrufparameter zu loggen, dann macht man das per Stringformatierung. Wie viele Logger hat denn Deine Klasse, so dass einer davon custom_logger heißt? Logger sind eines der wenigen Dinge, die man pro Modul als globale Variable definiert, weil ein Modul normalerweise die gröbste Einheit ist, auf der man den Log-Level festlegen will. Apropos Log-Level. Das was Du da loggst, ist maximal debug-Level. Zum Nachvollziehen von Fehlern in getesteten Programmen sollte es eigentlich reichen, Fehler und Warnungen zu loggen, wobei falsche Benutzereingaben zumindest Warnungen produzieren sollten.
Was das loggen von Benutzerdaten angeht, so gibt es in Deutschland einen ziemlich strengen Datenschutz. In Unternehmen kommt noch dazu, dass durch die Protokolle nicht auf das Verhalten von Mitarbeitern zurückgeschlossen werden kann. Das Protokollieren weiterer personenbezogener Daten bedarf im Normalfall der Zustimmung des Betriebsrats.
Re: Um jeden Preis protokollieren?
Verfasst: Sonntag 30. Oktober 2016, 14:19
von BlackJack
@Sophus: Was Du da protokollierst ist eher nicht `info` sondern `debug` und da würde man wichtige Werte mitprotokollieren. Insbesondere das untersuchen des Aufrufstacks würde ich sein lassen, das ist unter umständen ”teuer” und muss auch gar nicht zuverlässig funktionieren. Da fängt man ja an in den Interna der Implementierung zu wühlen. Ausserdem kann man den Logger schon so konfigurieren das der diese ganzen Informationen liefert, das muss man nicht manuell selber machen.
Als nächstes sollte man die Zeichenketten nicht selber formatieren sondern Platzhalter für den ``%``-Operator im ersten Argument schreiben und die Werte dafür als zusätzliche Argumente für den Log-Aufruf übergeben. Dann muss die Zeichenkette nämlich nicht erstellt werden wenn der Logger gar keine Ausgabe machen soll. Oder anders: Wenn Du das selber vor dem Aufruf machst, dann wird diese Arbeit getan, auch wenn der Logger das dann nirgends hinschreibt.
Den Abschnitt
When to use logging aus dem Logging-Howto in der Python-Dokumentation kennst Du schon‽
Ansonsten ist die Frage was Du mit den Ausgaben bezwecken willst und Du solltest Dich auch fragen ob Du das alles lesen willst was Du da protokollierst. Bei dem was Du da jetzt hast, und Du hast eine grosse Anwendung, und suchst dann irgendwas wichtiges im Protokoll: Durch wieviel Text bist Du bereit zu lesen bevor Du aufgibst.
Wenn Du wirklich alle Aufrufe haben willst, also nicht im normalen Betrieb sondern zur Fehlersuche, dann sollte man auch nicht manuell in jeder Funktion Log-Aufrufe schreiben. Dafür gibt es das `trace`-Modul. Und/oder `sys.settrace()`.
Re: Um jeden Preis protokollieren?
Verfasst: Sonntag 30. Oktober 2016, 14:41
von Sophus
Ich bringe mal meine "Einstellungen" mit, um zu zeigen, wie ich meinen Logger einstelle. Diese beiden Funktionen schreibe ich in ein separates Modul. In meinem Projekt importiere ich dieses Modul nur ein einziges Mal in mein sogenanntes "Main"-Modul. Danach instanziiere ich die Funktion, und überreiche es dann von Klasse zu Klasse als Argument weiter. Das heißt, sobald ich zum Beispiel mit einer View-Klasse arbeite, übergebe ich der jeweilen Klasse die Funktion.
@Siriu3: Aus diesem Grund nenne ich es bei mir dann "CustomLogger", eben weil ich die Log-Einstellungen für mich entsprechend eingestellt habe.
@BlackJack und Sirius: Ihr beide ratet mir davon ab, die Funktionennamen nicht händisch, in dem Falle mit dem inspect-Modul herauszuarbeiten, sondern man solle das dem Logger überlassen. Wo müsste ich das denn in mein separates Log-Modul einstellen, damit er mir den Namen der Funktion mitnimmt, wenn diese Aufgerufen wird?
Code: Alles auswählen
import logging
def set_custom_logger(app_info):
logger = logging.getLogger(app_info.dict_info["exe_name"])
return logger
def create_log_file(log_file_path, app_info):
# Call set_own_logger()-function to get
# the name of own logger
custom_logger = set_custom_logger(app_info)
# Create logger with 'spam_application'
custom_logger.setLevel(logging.DEBUG)
# Create file handler which logs even debug messages
handle_file = logging.FileHandler(log_file_path)
handle_file.setLevel(logging.DEBUG)
# First, create formatter, after then create formatter.
# Now formate the time in: year, month, day, hour, minute, secoond
# Then add the datefmt parameter at the end to the format (Formatter) for the Rotating FileHandler.
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d-%H:%M:%S')
# add formatter to the handlers
handle_file.setFormatter(formatter)
# add the handlers to the logger
custom_logger.addHandler(handle_file)
Re: Um jeden Preis protokollieren?
Verfasst: Sonntag 30. Oktober 2016, 15:40
von Sophus
Ok, mit einem Blick in die
HOW TO Dokumentation vom Logging-Modul habe ich mein Format angepasst. Derzeit bekomme ich also
Zeit, wann das Logging erstellt wurde,
Dateiname, wo die das Logging stattfand, dann
Funktionsname, wo die Logging aufgerufen wurde, dann die
Zeile, in welcher das Logging beginnt, dann
Name des Logging, dann
Levelname, also ob Debug, Info oder sonstiges, und dann die
Meldung, mit entsprechendem
Format des Datums.
An dieser Stelle danke an
BlackJack und
Sirius3, dass ihr mir darauf hingewiesen habt. Das erspart mir wirklich einige Wühlereien im inspect-Modul, nur um den richtige stack zu finden.
Nur muss ich jetzt noch irgendwie den richtige "Griff" finden, was man wirklich protokolliert. Wenn es nach mir ginge, dann einfach alles. Denn wenn Benutzer Probleme hat, und er mir dann die Log-Datei sendet, kann ich dann am besten nachvollziehen, was er "falsch" gemacht hat. Aber da es eine Verwaltungs-Software wird, müsste ich aufpassen, was ich mitschneiden darf. Siriu3 hat ja schon auf das Datenschutz-Thema hingewiesen. Das heißt, wenn ich eine Funktion protokollieren will, muss ich aufpassen, welches Argument, welches der Funktion übergeben wurde, ich mitschneiden darf und welche nicht.
Code: Alles auswählen
import logging
def set_custom_logger(app_info):
logger = logging.getLogger(app_info.dict_info["exe_name"])
return logger
def create_log_file(log_file_path, app_info):
# Call set_own_logger()-function to get
# the name of own logger
custom_logger = set_custom_logger(app_info)
#FORMAT = "[%(filename)s:%(lineno)s - %(funcName)20s() ] %(message)s"
#logging.basicConfig(format=FORMAT)
# Create logger with 'spam_application'
custom_logger.setLevel(logging.DEBUG)
# Create file handler which logs even debug messages
handle_file = logging.FileHandler(log_file_path)
handle_file.setLevel(logging.DEBUG)
# First, create formatter, after then create formatter.
# Now formate the time in: year, month, day, hour, minute, secoond
# Then add the datefmt parameter at the end to the format (Formatter) for the Rotating FileHandler.
formatter = logging.Formatter('[%(asctime)s] %(filename)s %(funcName)s():%(lineno)d: %(name)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d-%H:%M:%S')
# add formatter to the handlers
handle_file.setFormatter(formatter)
# add the handlers to the logger
custom_logger.addHandler(handle_file)
Re: Um jeden Preis protokollieren?
Verfasst: Sonntag 30. Oktober 2016, 16:04
von BlackJack
@Sophus: Das Du nur einen einzigen Logger für das gesamte Programm hast ist ungewöhnlich, ausser wenn es ein sehr kleines Programm ist. Wie Sirius3 ja schon geschrieben hat ist ein Logger pro Modul üblich, wo man als Argument `getLogger()` das `__name__`-Attribut des Moduls mitgibt. Dann braucht man das schon mal nicht mehr manuell in die Nachrichten einbauen.
Mit Deiner Begründung müsste *jeder* Logger dann custom_logger heissen. Denn das man das alles sehr flexibel anpassen kann ist ja völlig normal und alles andere als Custom. Man kann die ja nicht nur im Code direkt anpassen sondern auch Konfigurationsdateien verwenden.
Genau wegen dieser Konfigurationsmöglichkeiten will man nicht nur einen Logger für eine ganze Anwendung haben, sondern die in Modulen/Bereichen der Anwendung unabhängig konfigurieren können. Wieder um das gleiche Problem zu umgehen warum man nicht jeden Pups protokollieren sollte: Man ertrinkt ganz schnell in einer Datenflut und findet die Sachen nicht mehr, die einen *eigentlich* interessieren. Den DEBUG-Level will man beispielsweise selten für die ganze Anwendung einschalten, sondern nur für die Teile des Programms in denen man tatsächlich gerade einen Fehler sucht.
Was das Datenschutzthema angeht kann es sein, dass es auch schon problematisch werden kann überhaupt Funktionsaufrufe zu protokollieren. Denn das sind an sich ja auch schon Werte, anhand derer man nachvollziehen kann was der Benutzer gemacht hat. Wenn die Software beispielsweise bestimmte Verwaltungsvorgänge unterstützt könnte man alleine aus den Aufrufzeiten der entsprechenden Funktionen Mitarbeiter überwachen und auswerten wie lange sie für die Bearbeitung von einem Vorgang brauchen.
Wenn Du ”alles” Protokollieren möchtest, dann klink Dich in den Debugger ein und protokolliere wirklich jede ausgeführte Zeile mit allen Werten die da jeweils zugänglich sind. Und dann such darin am Ende mal ein bestimmtes Ereignis.
Re: Um jeden Preis protokollieren?
Verfasst: Sonntag 30. Oktober 2016, 19:22
von Sophus
BlackJack hat geschrieben:
Wenn Du ”alles” Protokollieren möchtest, dann klink Dich in den Debugger ein und protokolliere wirklich jede ausgeführte Zeile mit allen Werten die da jeweils zugänglich sind. Und dann such darin am Ende mal ein bestimmtes Ereignis.
Eben nicht! Ich möchte ja nicht alles protokollieren. Aber mir fehlt da das entsprechende "Gefühl" oder "Gespür" für sowas. Daher dachte ich, ihr kennt einige Python-Projekte, in denen eurer Meinung nach sehr gut protokolliert wird. Damit hoffe ich einfach, dass ich nachvollziehen kann, wie er/sie in seinem/ihren Programm protokolliert. Denn mein derzeitiger Gedankengang ist "Protokolliere einfach jeden Funktionsaufruf, damit du auch zuvor nachsehen kannst, wie es zum Fehler kam. Was hat der Benutzer DAVOR gemacht?". So ungefähr sieht mein Gedankengang aus.
Re: Um jeden Preis protokollieren?
Verfasst: Sonntag 30. Oktober 2016, 19:59
von BlackJack
@Sophus: Der Benutzer ruft ja nicht Deine Funktionen auf, sondern die ”Funktionen” die die Oberfläche ihm bietet. Wenn also ein Druck auf Schaltfläche X die Funktionen f, g, und h ausführt, also immer ausführt, weil die die Funktion für Schaltfläche X implementieren, dann macht es keinen Sinn immer wieder zu protokollieren das diese drei Funktionen ausgeführt werden. Das ist analog zu dem Argument von Sirius3 das es keinen Sinn macht innerhalb einer Funktion zu protokollieren was der Code macht wenn man das auch am Code ablesen kann.
Ich denke Du steckst hier zu viel Energie in etwas die besser in Unit-Tests angelegt wäre.
Re: Um jeden Preis protokollieren?
Verfasst: Sonntag 30. Oktober 2016, 21:29
von DasIch
Das einzige was Sinn macht zu protokollieren sind Interaktionen. Interaktionen mit dem Nutzer, Prozessen und Zeit.
Wenn du protokollierst dann solltest du nicht davon ausgehen dass ein Mensch den Log liest, dass ist häufig auch gar nicht praktikabel. Wenn du dich davon trennst kommt man zwangsläufig zu structured logging, womit du dich beschäftigen solltest, solltest du ernsthaft irgendwas protokollieren wollen.
Re: Um jeden Preis protokollieren?
Verfasst: Montag 31. Oktober 2016, 21:15
von Sophus
Speziell an
BlackJack und
Sirius3:
Ihr habt beide gemeint, dass es untypisch oder gar ungewöhnlich sei, dass man einen Logger für eine Anwendung hat, korrekt?
Aber mit dem nachfolgenden Quelltext ist es doch wieder typisch und gewöhnlich, dass man einen Logger hat. Denn ich übergebe der Klasse den Logger()-Objekt und den FileHandler()-Objekt. Und in der jeweiligen Methode der TestClass()-Klasse setze ich dann den entsprechenden Level. Auf diese Weise brauche ich nichts mehr auf der Modulebene JEDESMAL den Logger einstellen. Also, beim Testdurchlauf klappt es. In der func_foo()-Methode werden tatsächlich alle Warnungen und in der func_bar()-Methode alle Debuggs in die Datei geschrieben.
my_logger.py
Code: Alles auswählen
import logging.handlers as hand
import logging as logging
# create logger
lgr = logging.getLogger('myapp')
lgr.setLevel(logging.DEBUG)
# add a file handler
fh = logging.FileHandler('myapp.log')
fh.setLevel(logging.DEBUG)
# create a formatter and set the formatter for the handler.
formatter = logging.Formatter('[%(asctime)s] %(filename)s %(funcName)s():%(lineno)d: %(name)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d-%H:%M:%S')
fh.setFormatter(formatter)
# add the Handler to the logger
lgr.addHandler(fh)
Code: Alles auswählen
import traceback
import sys
import logging as logging
import my_logger
class TestClass(object):
def __init__(self, custom_logging, logging_file_handler):
self.custom_logging = custom_logging
self.logging_file_handler = logging_file_handler
def func_foo(self):
self.logging_file_handler.setLevel(logging.WARNING)
# You can now start issuing logging statements in your code
self.custom_logging.debug('debug message') # This won't print to myapp.log
self.custom_logging.info('info message') # Neither will this.
self.custom_logging.warn('Checkout this warning.') # This will show up in the log file.
self.custom_logging.error('An error goes here.') # and so will this.
self.custom_logging.critical('Something critical happened.') # and this one too.
def func_bar(self):
self.logging_file_handler.setLevel(logging.DEBUG)
# You can now start issuing logging statements in your code
self.custom_logging.debug('debug message') # This won't print to myapp.log
self.custom_logging.info('info message') # Neither will this.
self.custom_logging.warn('Checkout this warning.') # This will show up in the log file.
self.custom_logging.error('An error goes here.') # and so will this.
self.custom_logging.critical('Something critical happened.') # and this one too.
TestClass = TestClass(my_logger.lgr, my_logger.fh)
TestClass.func_foo()
TestClass.func_bar()
Re: Um jeden Preis protokollieren?
Verfasst: Montag 31. Oktober 2016, 21:34
von BlackJack
@Sophus: Auf Modulebene stellt man normalerweise auch nichts im Logger ein und innerhalb einzelner Funktionen schon gar nicht. Das fällt sowieso spätestens dann auf die Nase wenn man die Funktionen, die einen Logger verändern, in mehr als einem Thread ausführt. Die Konfiguration *aller* Logger im Programm passiert irgendwo im Hauptprogramm und spätestens bei entsprechend grossen Anwendungen auch nicht im Code sondern über Konfigurationsdateien.
Re: Um jeden Preis protokollieren?
Verfasst: Montag 31. Oktober 2016, 23:39
von Sophus
@BlackJack: Inwiefern würde ich dann auf die Nase fallen? Hat das logging-Modul ein Problem damit, wenn man den Handler ständig ändert? Aber um das Problem zu umgehen, könnte man den Handler dann Klassen-weit einmalig ändern und man wäre damit aus dem Schneider.
Re: Um jeden Preis protokollieren?
Verfasst: Montag 31. Oktober 2016, 23:53
von BlackJack
@Sophus: Man hat dann die üblichen Probleme mit globalem Zustand und Nebenläufigkeit. Eine Funktion stellt auf INFO und gleich drauf die nächste auf DEBUG und schon loggt auch die erste mit DEBUG obwohl sie ja eigentlich INFO wollte. Solange bis die nächste Funktion im ersten Thread auf ERROR stellt und damit die die eigentlich DEBUG wollte. Selbst bei nur einem Thread ist das viel zu fehleranfällig wenn beliebige Funktionen da dran rumschrauben und damit ja auch allen zeitlich folgenden Code beeinflussen. Man müsste also jedes mal merken welchen Level man hatte, den ändern und am Ende der Funktion wieder zurück stellen auf das was man vorher hatte, oder man hat nicht mehr wirklich unter Kontrolle welcher Teil zur Laufzeit mit welchem Level protokolliert.
Wie willst Da ”klassenweit” irgend etwas ändern?
Re: Um jeden Preis protokollieren?
Verfasst: Dienstag 1. November 2016, 00:13
von Sophus
@BlackJack: In meinem oben Beispiel werden die Levels ja in jeder Funktion geändert. Aber wie Siriu3 anmerkte, sind Module so grobe Einheiten. Nun, wenn ich in meinem oben Beispiel das Handler-Objekt an die TestClasse() übergeben habe, dann werden die Levels nicht eben in jeder Funktion geändert, sondern in der Klasse. Auf der Ebene, auf welche Attribute erzeugt werden, in dem sie an das "self" gebunden werden.
Re: Um jeden Preis protokollieren?
Verfasst: Dienstag 1. November 2016, 00:50
von Sophus
@BlackJack: So meinte ich das in Etwa: (Ich weiß, du magst diese Getter nicht in der CustomLogging-Klasse. Aber ich empfinde das eher angenehm, weil man dabei nicht dazu neigt, etwas ändern zu wollen in der CustomLogging-Klasse. So bekommt man nur die Objekte. In der TestClass-Klasse wird der Handler dann Klassen-weit einmalig geändert und damit gearbeitet.)
my_logger.py
Code: Alles auswählen
import logging.handlers as hand
import logging as logging
class CustomLogging(object):
def __init__(self):
# create logger
self.lgr = logging.getLogger('myapp')
self.lgr.setLevel(logging.DEBUG)
# add a file handler
self.fh = logging.FileHandler('myapp.log')#
#fh.setLevel(logging.DEBUG)
# create a formatter and set the formatter for the handler.
formatter = logging.Formatter('[%(asctime)s] %(filename)s %(funcName)s():%(lineno)d: %(name)s - %(levelname)s - %(message)s', datefmt='%Y-%m-%d-%H:%M:%S')
self.fh.setFormatter(formatter)
# add the Handler to the logger
self.lgr.addHandler(self.fh)
def logging_file_handler(self):
return self.fh
def logging_object(self):
return self.lgr
main_log.py
Code: Alles auswählen
import traceback
import sys
import logging as logging
from my_logger import CustomLogging
class TestClass(object):
def __init__(self, custom_logging, logging_file_handler):
self.custom_logging = custom_logging
self.logging_file_handler = logging_file_handler
#self.logging_file_handler.setLevel(logging.DEBUG)
self.logging_file_handler.setLevel(logging.WARNING)
def func_foo(self):
# You can now start issuing logging statements in your code
self.custom_logging.debug('debug message') # This won't print to myapp.log
self.custom_logging.info('info message') # Neither will this.
self.custom_logging.warn('Checkout this warning.') # This will show up in the log file.
self.custom_logging.error('An error goes here.') # and so will this.
self.custom_logging.critical('Something critical happened.') # and this one too.
my1 = CustomLogging()
my2 = my1.logging_object()
my3 = my1.logging_file_handler()
TestClass = TestClass(my2, my3)
TestClass.func_foo()
Re: Um jeden Preis protokollieren?
Verfasst: Dienstag 1. November 2016, 00:54
von BlackJack
@Sophus: Wo der Logger herkommt, und ob Du den an das Objekt bindest, hat dabei keine Bedeutung. Eine Änderung passiert nicht nur ”in der Klasse”, was immer das bedeuten mag bei Attributen die auf dem Objekt existieren, und selbst wenn es tatsächlich an die Klasse gebunden wird, die Änderung des Levels ist *global* sichtbar für alle Stellen im Code die diesen Logger verwenden. Das gilt analog für den Handler.
Sirius3 hat nicht angemerkt das Module grobe Einheiten sind, sondern das es gängige Praxis ist pro Modul einen Logger *in* dem Modul auf Modulebene zu erstellen.
Re: Um jeden Preis protokollieren?
Verfasst: Dienstag 1. November 2016, 01:30
von Sophus
@BlackJack: Ich habe ja im letzten Beitrag ein Beispiel hoch geladen. Inwiefern wäre das Handler-Objekt und das Logger-Objekt global? In der TestClass-Klasse hätte dies in der Tat gewisse Sichtbarkeit. Aber das ist ja nicht schlimm? Denn sobald man grundsätzlich einer Klasse Argumente übergibt und in der Klasse werden diese Argumente zum Attribut, damit man auf die Informationen zugreifen kann. Nichts anderes mache ich?
Re: Um jeden Preis protokollieren?
Verfasst: Dienstag 1. November 2016, 11:14
von BlackJack
@Sophus: Der Logger existiert im ganzen Programm nur einmal. Wenn Du das Loglevel änderst dann ist das eine globale Zustandsänderung. An jeder Stelle im Programm die den Logger verwendet, wird dann das geänderte Loglevel verwendet. Das Du den an ein Attribut bindest bewirkt nicht auf magische Weise das der Logger sich dann nur innerhalb von Objekten dieser Klasse anders verhält als überall anders von wo aus auf ihn zugegriffen wird.
Edit: Vielleicht liegt Dein Problem ja bei `getLogger()` → da übergibst Du immer den gleichen Namen also wird da auch immer das *selbe* `Logger`-Exemplar zurückgegeben und nicht bei jedem Aufruf ein neues. `getLogger()` erstellt nur ein neues Objekt wenn es noch keinen für den Namen gibt den man als Argument übergibt. Ansonsten wird der bereits vorhandene zurückgegeben. Du kannst zwar verschiedene `CustomLogger` erstellen, die benutzen aber *alle* das selbe `Logger`-Exemplar. Und Du erstellst immer neue `FileHandler` mit dem gleichen Dateinamen und fügst die *zusätzlich* zu dem *einen* `Logger` hinzu. Das ist ein Fehler würde ich mal sagen. Du arbeitest mit Deiner Klasse letztendlich gegen das System wie `logging` funktioniert.
Selbst wenn es nur einen im ganzen Programm geben würde, bräuchte man keine eigene Klasse sondern würde sich einmal am Anfang des Programms mit ``getLogger('myapp')`` den Logger geben/erstellen lassen und ihn konfigurieren und sich dann an anderen Stellen im Programm wo man darüber Loggen möchte sich den konfigurierten Logger mit einem ``getLogger('myapp')``-Aufruf geben lassen.
Re: Um jeden Preis protokollieren?
Verfasst: Dienstag 1. November 2016, 17:23
von Sophus
@BlackJack: Du schreibst ja, dass es im Programm nur einen Logger gibt. Wenn man also den LogLevel in einem anderen Modul anwenden will, heißt es, dass ich dann einen neuen, weiteren Logger hinzufügen muss, damit ich den Level nicht global ändere? Worauf ich hinaus will. Wenn es nur einen Logger im Programm gibt (was ja auch gut ist), und ich ändere dann den LogLevel in einem anderen Modul, dann ändere ich den Level für das gesamt existierenden Logger im Programm.
Re: Um jeden Preis protokollieren?
Verfasst: Dienstag 1. November 2016, 17:35
von DasIch
Was du machst ist vollkommen absurd. Logging konfiguriert man an einer Stelle, einmalig für die gesamte Anwendung. Code der Log Nachrichten erzeugt hat nicht zu interessieren welche Nachrichten, in welchem Format gelesen werden und wie sie ausgegeben werden.