2016-11-08 1 views
1

Ich verwende das folgende Modul, um Ereignisse in meinen Modulen zu protokollieren. Ich nenne es wie folgt:Verwenden von Python-Protokollierung von mehreren Modulen mit dem Schreiben in eine Datei und RotatingFileHandler

Modul 1

from tools.debug_logger import debug_logger 
self.logger = debug_logger().start_logger('module1') 
self.logger.debug("Top left corner found") 

module2:

from tools.debug_logger import debug_logger 
self.logger = debug_logger().start_logger('module2') 
self.logger.debug("Top left corner found") 

und hier die Datei /tools/debug_logger.py

import logging, logging.handlers 
import sys 
class debug_logger(object): 
    def start_logger(self,name): 
     logger = logging.getLogger(name) 
     logger.setLevel(logging.DEBUG) 
     if not len(logger.handlers): 
      fh = logging.handlers.RotatingFileHandler('log/pokerprogram.log', maxBytes=1000000, backupCount=10) 
      fh.setLevel(logging.DEBUG) 
      fh2 = logging.handlers.RotatingFileHandler('log/pokerprogram_info_only.log', maxBytes=1000000, backupCount=5) 
      fh2.setLevel(logging.INFO) 
      er = logging.handlers.RotatingFileHandler('log/errors.log', maxBytes=2000000, backupCount=2) 
      er.setLevel(logging.WARNING) 
      ch = logging.StreamHandler(sys.stdout) 
      ch.setLevel(1) 
      fh.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')) 
      fh2.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')) 
      er.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')) 
      ch.setFormatter(logging.Formatter('%(name)s - %(levelname)s - %(message)s')) 
      logger.addHandler(fh) 
      logger.addHandler(fh2) 
      logger.addHandler(ch) 
      logger.addHandler(er) 
     return logger 

Alles funktioniert gut und ich Log-Dateien für die jeweiligen Ebenen abrufen, aber wenn der RotatingFileHandler aufgerufen wird, erhalte ich manchmal einen Fehler. Es ist, als ob verschiedene Instanzen gleichzeitig die Rotation durchführen möchten, obwohl ich mir ziemlich sicher bin, dass dies nicht passieren sollte, da ich sicherstelle, dass es nur 1 Handler mit if not len(logger.handlers) gibt, wie hier empfohlen: Duplicate log output when using Python logging module.

Jeder Vorschlag, der diese Dateizugriffsverletzung während der Rotation verursachen könnte, wäre willkommen.

Ich glaube, dass der Erlaubnisfehler auftritt, weil, wenn die Umdrehung auftritt, andere Module noch in die Akte schreiben.

Was wäre der beste Weg, Logging von mehreren Modulen zu machen, wenn ich in eine Datei schreibe und diesen RotatingFileHandler verwende? Gibt es Best Practices?

+0

Sie tun es falsch. Ihre Module sollten * keine * Handler definieren oder die Protokollierungsstufen festlegen. Nur die Haupt-Executable sollte die Handler und die zu verwendenden Level einrichten ... das vereinfacht alles, da nach dem Ausführen immer nur ein Handler für diese Datei existiert und es keine Chance gibt, dass dieser Fehler auftritt. – Bakuriu

+0

Die mit 'wenn nicht len ​​(logger.handlers):' Ich stelle sicher, dass es nur 1 Handler gibt. Was ist ein besserer Weg, dies zu tun? – Nickpick

+0

Nein, tust du nicht. Weil module1 und module2 unterschiedliche Namen haben, was bedeutet, dass die Logger, die von 'getLogger' zurückgegeben werden, unterschiedlich sind und daher fügen Sie einen Handler für module1 und einen für module2 hinzu. So, sicher, dass ein einzelner Logger nicht mehr als einen Handler hat, aber Sie * haben * zwei Handler, die dieselbe Datei verwenden, und dies verursacht den Fehler, den Sie sehen. – Bakuriu

Antwort

5

Ich glaube, Sie bekommen Ihre Protokollierung falsch eingerichtet. Die empfohlene Methode zum Einrichten der Protokollierung ist nicht Definieren Sie alle Handler noch Protokollierungsstufen in die Module, sondern definieren Sie die gesamte Konfiguration in der Hauptdatei.

Zum Beispiel in module1.py:

import logging 

logger = logging.getLogger(__name__) 

# use logger.info/logger.debug etc. 

In module2.py setzen Sie genau den gleichen Code:

import logging 

logger = logging.getLogger(__name__) 

# use logger.info/logger.debug etc. 

Beachten Sie, dass __name__ der Modulname ist, so dass es so etwas wie package.module1 oder package.module2 sein wird. Die Verwendung von Punktnamen erzeugt automatisch Hierarchien von Loggern, deshalb ist es üblich, nur den __name__ des Moduls zu verwenden, um den Logger zu bekommen.

Es ist nicht erforderlich, dass module1 und module2 alles andere enthalten, was mit der Protokollierung zu tun hat. Sie sollten nicht entscheiden, wo der Logging-Ausgang geht oder seine Ebene, weil dies etwas ist, wer die Anwendung startet, sollte steuern. Daher wird es am besten in der Hauptdatei behandelt.

Jetzt in Ihrem Hauptprogramm definieren Sie Ihre Handler:

import logging, logging.handlers 

fh = logging.handlers.RotatingFileHandler('log/pokerprogram.log', maxBytes=1000000, backupCount=10) 
fh.setLevel(logging.DEBUG) 
fh2 = logging.handlers.RotatingFileHandler('log/pokerprogram_info_only.log', maxBytes=1000000, backupCount=5) 
fh2.setLevel(logging.INFO) 
er = logging.handlers.RotatingFileHandler('log/errors.log', maxBytes=2000000, backupCount=2) 
er.setLevel(logging.WARNING) 
ch = logging.StreamHandler(sys.stdout) 
ch.setLevel(1) 
fh.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')) 
fh2.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')) 
er.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')) 
ch.setFormatter(logging.Formatter('%(name)s - %(levelname)s - %(message)s')) 

Und schließlich fügen Sie nur die Handler zum root-Logger und stellen Sie das Niveau des Root-Logger auf das niedrigste Niveau unter den Handler:

root = logging.getLogger() 
root.setLevel(logging.DEBUG) 
# alternatively: 
# root.setLevel(min([fh.level, fh2.level, ch.level, er.level]) 

root.addHandler(fh) 
root.addHandler(fh2) 
root.addHandler(ch) 
root.addHandler(er) 

Dies funktioniert aufgrund der hierarchischen Natur der Logger. Wenn module1.logger.debug aufgerufen wird, wenn der Logger keine Handler hat, übergibt es den Protokolleintrag an seinen Parent-Logger, der dies weiterhin tun wird, bis der Stammlogger, der schließlich seine Handler verwendet, um den Protokolldatensatz zu behandeln.

Es ist notwendig, auch die Root-Logger-Ebene festgelegt, da es standardmäßig WARNING, während andere Loggern standardmäßig NOTSET (die die Delegation bereits erwähnt verursacht).

Alternativ fügen Sie die gleichen Handler beiden Modullogger explizit:

from <package> import module1, module2 

module1.logger.setLevel(logging.DEBUG) 
module2.logger.setLevel(logging.DEBUG) 

module1.logger.addHandler(fh) 
module2.logger.addHandler(fh) 
module1.logger.addHandler(fh2) 
module2.logger.addHandler(fh2) 
module1.logger.addHandler(ch) 
module2.logger.addHandler(ch) 
module1.logger.addHandler(er) 
module2.logger.addHandler(er) 

Es Fügen Sie dazu die gleiche Handler-Objekt zu mehreren Loggern keinen Schaden. Dadurch wird sichergestellt, dass die Handler nicht gleichzeitig versuchen, die Datei zu drehen.

+0

Können Sie bestätigen, dass es erforderlich ist, die Protokollierungsstufe in jedem Modul erneut festlegen, andernfalls wird es auf Protokollierung voreingestellt.WARNING? – Nickpick

+0

@nickpick Ah, tut mir leid, ich habe vergessen hinzuzufügen: Sie können nur die Ebene der Root-Logger einstellen. Ich werde das in der Antwort bearbeiten. – Bakuriu

+0

@nickpick Nehmen Sie ein llok in der Dokumentation für ['setLevel'] (https://docs.python.org/3/library/logging.html#logging.Logger.setLevel). Es besagt, dass der Root-Logger standardmäßig auf 'WARNING' gesetzt ist, während andere Logger standardmäßig auf' NOTSET' gesetzt sind. Wenn ein Logger auf der Ebene "NOTSET" ist, findet die Delegierung an die Parent-Logger statt. Wenn Sie also die Handler direkt zu den beiden Modul-Loggern hinzufügen, müssen Sie deren Level ändern, wenn Sie sie zum Root-Logger hinzufügen Sie sollten ihr Niveau nicht einstellen. – Bakuriu

Verwandte Themen