it-swarm.com.de

Python-Protokollierung (Funktionsname, Dateiname, Zeilennummer) unter Verwendung einer einzelnen Datei

Ich versuche zu lernen, wie eine Anwendung funktioniert. Dazu füge ich Debug-Befehle als erste Zeile des Hauptteils jeder Funktion ein, um den Namen der Funktion sowie die Zeilennummer (innerhalb des Codes) zu protokollieren, in der ich eine Nachricht an die Protokollausgabe sende. Da diese Anwendung aus vielen Dateien besteht, möchte ich eine einzelne Protokolldatei erstellen, um den Steuerungsfluss der Anwendung besser zu verstehen.

Folgendes weiß ich:

  1. um den Funktionsnamen zu erhalten, kann ich function_name.__name__ verwenden, aber ich möchte den Funktionsnamen nicht verwenden (damit ich schnell eine generische Log.info("Message") in den Hauptteil aller Funktionen kopieren und einfügen kann). Ich weiß, dass dies in C mit dem Makro __func__ möglich ist, bin mir aber bei Python nicht sicher.

  2. um den Dateinamen und die Zeilennummer zu erhalten, habe ich gesehen, dass (und ich glaube, dass) meine Anwendung die Python-Funktion locals() verwendet, aber in einer Syntax, die mir nicht vollständig bewusst ist, z. B .: options = "LOG.debug('%(flag)s : %(flag_get)s' % locals()), und ich habe versucht, die Funktion like LOG.info("My message %s" % locals()) zu verwenden, die so etwas erzeugt {'self': <__main__.Class_name object at 0x22f8cd0>}. Irgendwelche Eingaben dazu bitte?

  3. Ich weiß, wie man die Protokollierung verwendet und einen Handler hinzufügt, um eine Datei zu protokollieren, aber ich bin nicht sicher, ob eine einzelne Datei verwendet werden kann, um alle Protokollmeldungen in der richtigen Reihenfolge der Funktionsaufrufe im Projekt aufzuzeichnen.

Ich würde mich über jede Hilfe sehr freuen.

Vielen Dank!

82
user1126425

Sie haben hier ein paar geringfügig verwandte Fragen.

Ich fange mit dem einfachsten an: (3). Mit logging können Sie alle Aufrufe zu einer einzigen Protokolldatei oder einem anderen Ausgabeziel zusammenfassen: Sie befinden sich in der Reihenfolge, in der sie im Prozess aufgetreten sind.

Als nächstes: (2). locals() liefert ein Diktat des aktuellen Bereichs. Daher haben Sie in einer Methode, die keine anderen Argumente hat, self im Gültigkeitsbereich, der einen Verweis auf die aktuelle Instanz enthält. Der Trick, der Sie überwältigt, ist die String-Formatierung, die ein Dict als RHS des %-Operators verwendet. "%(foo)s" % bar wird durch den Wert von bar["foo"] ersetzt.

Schließlich können Sie einige Introspektions-Tricks verwenden, ähnlich den von pdb verwendeten, die mehr Informationen protokollieren können:

def autolog(message):
    "Automatically log the current function details."
    import inspect, logging
    # Get the previous frame in the stack, otherwise it would
    # be this function!!!
    func = inspect.currentframe().f_back.f_code
    # Dump the message + the name of this function to the log.
    logging.debug("%s: %s in %s:%i" % (
        message, 
        func.co_name, 
        func.co_filename, 
        func.co_firstlineno
    ))

Dadurch werden die übergebene Nachricht sowie der (ursprüngliche) Funktionsname, der Dateiname, in dem die Definition erscheint, und die Zeile in dieser Datei protokolliert. Schauen Sie sich an inspect - Inspect live objects für weitere Details.

Wie ich bereits in meinem Kommentar erwähnt habe, können Sie jederzeit in eine pdb interactive Debugging-Eingabeaufforderung wechseln, indem Sie die Zeile import pdb; pdb.set_trace() einfügen und Ihr Programm erneut ausführen. Auf diese Weise können Sie den Code schrittweise durchgehen und die Daten nach Belieben prüfen.

21

Die richtige Antwort ist, die bereits vorhandene Variable funcName zu verwenden 

import logging
logger = logging.getLogger('root')
FORMAT = "[%(filename)s:%(lineno)s - %(funcName)20s() ] %(message)s"
logging.basicConfig(format=FORMAT)
logger.setLevel(logging.DEBUG)

Dann, wo immer Sie möchten, fügen Sie einfach Folgendes hinzu:

logger.debug('your message') 

Beispielausgabe eines Skripts, an dem ich gerade arbeite:

[invRegex.py:150 -          handleRange() ] ['[A-Z]']
[invRegex.py:155 -     handleRepetition() ] [[<__main__.CharacterRangeEmitter object at 0x10ba03050>, '{', '1', '}']]
[invRegex.py:197 -          handleMacro() ] ['\\d']
[invRegex.py:155 -     handleRepetition() ] [[<__main__.CharacterRangeEmitter object at 0x10ba03950>, '{', '1', '}']]
[invRegex.py:210 -       handleSequence() ] [[<__main__.GroupEmitter object at 0x10b9fedd0>, <__main__.GroupEmitter object at 0x10ba03ad0>]]
374

funcname, linename und lineno liefern Informationen über die letzte Funktion, die die Protokollierung durchgeführt hat.

Wenn Sie einen Wrapper für Logger (z. B. Singleton Logger) haben, funktioniert die Antwort von @ synthesizerpatel möglicherweise nicht für Sie.

Um die anderen Anrufer in der Anrufliste herauszufinden, können Sie Folgendes tun:

import logging
import inspect

class Singleton(type):
    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances:
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]

class MyLogger(metaclass=Singleton):
    logger = None

    def __init__(self):
        logging.basicConfig(
            level=logging.INFO,
            format="%(asctime)s - %(threadName)s - %(message)s",
            handlers=[
                logging.StreamHandler()
            ])

        self.logger = logging.getLogger(__+ '.logger')

    @staticmethod
    def __get_call_info():
        stack = inspect.stack()

        # stack[1] gives previous function ('info' in our case)
        # stack[2] gives before previous function and so on

        fn = stack[2][1]
        ln = stack[2][2]
        func = stack[2][3]

        return fn, func, ln

    def info(self, message, *args):
        message = "{} - {} at line {}: {}".format(*self.__get_call_info(), message)
        self.logger.info(message, *args)
0
DeveScie