658 Stimmen

Logger Konfiguration zum Protokollieren in Datei und zum Ausdrucken auf die Standardausgabe

Ich verwende das Python-Logging-Modul, um einige Debug-Strings in eine Datei zu protokollieren, was ziemlich gut funktioniert. Jetzt möchte ich zusätzlich dieses Modul verwenden, um die Strings auch auf stdout auszugeben. Wie mache ich das? Um meine Strings in eine Datei zu protokollieren, verwende ich folgenden Code:

import logging
import logging.handlers
logger = logging.getLogger("")
logger.setLevel(logging.DEBUG)
handler = logging.handlers.RotatingFileHandler(
    LOGFILE, maxBytes=(1048576*5), backupCount=7
)
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
handler.setFormatter(formatter)
logger.addHandler(handler)

und dann rufe eine Logger-Funktion wie

logger.debug("Ich werde in die Datei geschrieben")

Vielen Dank für die Hilfe!

0 Stimmen

Neugierig, wenn Sie den Level auf DEBUG ändern, wird es immer noch auf stdout UND in die Datei protokolliert? Ich glaube, es passiert nur, wenn es auf INFO eingestellt ist. Bitte korrigieren Sie mich, wenn ich falsch liege.

1 Stimmen

7voto

Lexxer Punkte 81

Nachdem ich Waterboys Code immer wieder in mehreren Python-Paketen verwendet habe, habe ich ihn schließlich in ein eigenständiges, winziges Python-Paket umgewandelt, das Sie hier finden können:

https://github.com/acschaefer/duallog

Der Code ist gut dokumentiert und einfach zu verwenden. Laden Sie einfach die .py Datei herunter und fügen Sie sie in Ihr Projekt ein oder installieren Sie das gesamte Paket über pip install duallog.

0 Stimmen

Aus irgendeinem Grund wird weder in der Konsole noch in der Datei (ist leer) protokolliert.

1voto

djvg Punkte 7864

Auch wenn die Frage speziell nach einer Logger-Konfiguration fragt, gibt es einen alternativen Ansatz, der keine Änderungen an der logging-Konfiguration erfordert und auch keine Umleitung von stdout erfordert.

Vielleicht etwas vereinfacht, aber es funktioniert:

def log_and_print(message: str, level: int, logger: logging.Logger):
    logger.log(level=level, msg=message)  # wie gewohnt protokollieren
    print(message)  # standardmäßig Ausgabe nach stdout

Statt z.B. logger.debug('etwas') rufen wir jetzt log_and_print(message='etwas', level=logging.DEBUG, logger=logger).

Wir können das auch etwas erweitern, damit es nur dann printt, wenn es notwendig ist:

def log_print(message: str, level: int, logger: logging.Logger):
    # Nachricht normal protokollieren
    logger.log(level=level, msg=message)
    # nur nach stdout drucken, wenn die Nachricht nicht nach stdout protokolliert wird
    msg_logged_to_stdout = False
    current_logger = logger
    while current_logger and not msg_logged_to_stdout:
        is_enabled = current_logger.isEnabledFor(level)
        logs_to_stdout = any(
            getattr(handler, 'stream', None) == sys.stdout
            for handler in current_logger.handlers
        )
        msg_logged_to_stdout = is_enabled and logs_to_stdout
        if not current_logger.propagate:
            current_logger = None
        else:
            current_logger = current_logger.parent            
    if not msg_logged_to_stdout:
        print(message)

Dies überprüft den Logger und seine Eltern auf Handler, die nach stdout streamen, und überprüft, ob der Logger für das angegebene Level aktiviert ist.

Beachten Sie, dass dies nicht für die Leistung optimiert wurde.

0voto

Shayan Amani Punkte 4881

Ich habe die Umleitung von Logs und Ausgaben gleichzeitig auf eine Datei auf der Festplatte, stdout und stderr mithilfe des folgenden Moduls (das Gist ist auch hier verfügbar):

import logging
import pathlib
import sys

from ml.common.const import LOG_DIR_PATH, ML_DIR

def create_log_file_path(file_path, root_dir=ML_DIR, log_dir=LOG_DIR_PATH):
    path_parts = list(pathlib.Path(file_path).parts)
    relative_path_parts = path_parts[path_parts.index(root_dir) + 1:]
    log_file_path = pathlib.Path(log_dir, *relative_path_parts)
    log_file_path = log_file_path.with_suffix('.log')
    # Erstelle die Verzeichnisse und die Datei selbst
    log_file_path.parent.mkdir(parents=True, exist_ok=True)
    log_file_path.touch(exist_ok=True)
    return log_file_path

def set_up_logs(file_path, mode='a', level=logging.INFO):
    log_file_path = create_log_file_path(file_path)
    logging_handlers = [logging.FileHandler(log_file_path, mode=mode),
                        logging.StreamHandler(sys.stdout)]
    logging.basicConfig(
        handlers=logging_handlers,
        format='%(asctime)s %(name)s %(levelname)s %(message)s',
        level=level
    )

class OpenedFileHandler(logging.FileHandler):

    def __init__(self, file_handle, filename, mode):
        self.file_handle = file_handle
        super(OpenedFileHandler, self).__init__(filename, mode)

    def _open(self):
        return self.file_handle

class StandardError:
    def __init__(self, buffer_stderr, buffer_file):
        self.buffer_stderr = buffer_stderr
        self.buffer_file = buffer_file

    def write(self, message):
        self.buffer_stderr.write(message)
        self.buffer_file.write(message)

class StandardOutput:
    def __init__(self, buffer_stdout, buffer_file):
        self.buffer_stdout = buffer_stdout
        self.buffer_file = buffer_file

    def write(self, message):
        self.buffer_stdout.write(message)
        self.buffer_file.write(message)

class Logger:
    def __init__(self, file_path, mode='a', level=logging.INFO):
        self.stdout_ = sys.stdout
        self.stderr_ = sys.stderr

        log_file_path = create_log_file_path(file_path)
        self.file_ = open(log_file_path, mode=mode)

        logging_handlers = [OpenedFileHandler(self.file_, log_file_path,
                                              mode=mode),
                            logging.StreamHandler(sys.stdout)]
        logging.basicConfig(
            handlers=logging_handlers,
            format='%(asctime)s %(name)s %(levelname)s %(message)s',
            level=level
        )

    # Überschreibt die write() Methode der stdout- und stderr-Puffer
    def write(self, message):
        self.stdout_.write(message)
        self.stderr_.write(message)
        self.file_.write(message)

    def flush(self):
        pass

    def __enter__(self):
        sys.stdout = StandardOutput(self.stdout_, self.file_)
        sys.stderr = StandardError(self.stderr_, self.file_)

    def __exit__(self, exc_type, exc_val, exc_tb):
        sys.stdout = self.stdout_
        sys.stderr = self.stderr_
        self.file_.close()

Als Kontext-Manager geschrieben, können Sie die Funktionalität ganz einfach zu Ihrem Python-Skript hinzufügen, indem Sie eine zusätzliche Zeile hinzufügen:

from logger import Logger

...

if __name__ == '__main__':
    with Logger(__file__):
        main()

-5voto

JonM Punkte 3

Für 2.7, versuchen Sie folgendes:

fh = logging.handlers.RotatingFileHandler(LOGFILE, maxBytes=(1048576*5), backupCount=7)

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X