100 votes

Comment définir différents niveaux pour différents gestionnaires de journaux python

J'ai lu quelques articles à ce sujet mais je suis encore confus. J'ai cette configuration de journalisation:

import logging

class MongoHandler(logging.Handler):
    def __init__(self):
        logging.Handler.__init__(self)
        from pymongo import Connection
        self.db = Connection('db_server').db_name

    def emit(self, record):
        try:
            self.db.Logging.save(record.__dict__)
        except:
            print 'Erreur de journalisation: Impossible d'enregistrer l'entrée du journal dans la base de données'

mh = MongoHandler()
sh = logging.StreamHandler()
formatter = logging.Formatter('%(asctime)s - %(threadName)s - %(levelname)s - %(message)s')
sh.setFormatter(formatter)
log = logging.getLogger('DeviceMonitor_%s' % hostname)
log.addHandler(mh)
log.addHandler(sh)
log.setLevel(logging.INFO)

Je veux pouvoir définir un niveau différent pour le StreamHandler et le MongoHandler. Est-ce possible ou dois-je avoir un deuxième objet Logger?

140voto

GrantVS Points 611

Vous pouvez définir un niveau de journalisation différent pour chaque gestionnaire de journalisation, mais il semble que vous devrez définir le niveau du journal à celui le plus "bas". Dans l'exemple ci-dessous, j'ai défini le journal sur DEBUG, le gestionnaire de flux sur INFO et le TimedRotatingFileHandler sur DEBUG. Ainsi, le fichier contient des entrées DEBUG et le flux ne renvoie que INFO. Vous ne pouvez pas rediriger uniquement DEBUG vers un gestionnaire et uniquement INFO vers un autre. Pour cela, vous aurez besoin d'un autre journal.

logger = logging.getLogger("mylog")
formatter = logging.Formatter(
    '%(asctime)s | %(name)s |  %(levelname)s: %(message)s')
logger.setLevel(logging.DEBUG)

stream_handler = logging.StreamHandler()
stream_handler.setLevel(logging.INFO)
stream_handler.setFormatter(formatter)

logFilePath = "my.log"
file_handler = logging.handlers.TimedRotatingFileHandler(
    filename=logFilePath, when='midnight', backupCount=30)
file_handler.setFormatter(formatter)
file_handler.setLevel(logging.DEBUG)

logger.addHandler(file_handler)
logger.addHandler(stream_handler)

logger.info("Début");
try:
    x = 14
    y = 0
    z = x / y
except Exception as ex:
    logger.error("Opération échouée.")
    logger.debug(
        "Rencontré {0} lors de la tentative de calcul.".format(ex))

logger.info("Fin");

13voto

Philippe Gachoud Points 209

J'avais besoin de temps pour comprendre le point

  1. Définissez le journal général ci-dessous vos sous-journaux (gestionnaires) (votre résultat de logging.getLogger())
  2. Définissez les niveaux de vos sous-journeaux sur un niveau égal ou supérieur à votre journal général

8voto

Mario Points 37

En plus de la réponse de GrantVS sur Stack Overflow :

J'ai dû utiliser

logging.basicConfig(level=logging.DEBUG)

pour que cela fonctionne. Sinon, excellente réponse, merci !

1voto

rdmolony Points 173

Avait le même problème mais la solution n'a pas fonctionné pour iPython car QtConsole crée automatiquement un gestionnaire sans niveau défini :

import logging                                                                                                                               
root = logging.getLogger()                                                                                                                   
root.handlers                                                                                                                                
Out: [ (NOTSET)>]

En conséquence, iPython imprimait à la fois DEBUG et INFO dans la console malgré des niveaux différents pour mon gestionnaire de fichier et mon gestionnaire de flux.

Ce fil de discussion a souligné ce problème pour moi : Le module de journalisation n'imprime pas dans IPython

J'ai créé un module d'assistance (grandement aidé par ce fil de discussion sur Stack!) appelé custom_logging.py pour rendre la journalisation plus pratique dans d'autres modules :

import logging
from pathlib import Path
import sys

def _add_stream_handler(logger: logging.Logger):

    stream_handler = logging.StreamHandler()
    formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s')
    stream_handler.setFormatter(formatter)
    stream_handler.setLevel(logging.INFO)

    logger.addHandler(stream_handler)

    return logger

def _add_file_handler(logger: logging.Logger, log_path: Path):

    file_handler = logging.FileHandler(log_path, mode='w')
    formatter = logging.Formatter(
        fmt='%(asctime)s %(name)-12s %(levelname)-8s %(message)s', datefmt='%m-%d %H:%M')

    file_handler.setFormatter(formatter)
    file_handler.setLevel(logging.DEBUG)

    logger.addHandler(file_handler)

    return logger

def create_logger(root_dir: Path, caller: str) -> logging.Logger:

    log_path = root_dir / 'logs' / f'{caller}.log'
    logger = logging.getLogger(caller)
    root = logging.getLogger()

    logger.setLevel(logging.DEBUG)

    # Si les gestionnaires n'ont pas déjà été lancés...
    if not len(logger.handlers):

        _add_file_handler(logger=logger, log_path=log_path)
        _add_stream_handler(logger=logger)

        logger.info('Journalisation démarrée.')

    # Supprimer le gestionnaire stderr de Qtconsole
    # ... car il journalise automatiquement les niveaux DEBUG et INFO vers stderr
    if root.handlers:
        root.handlers = []

    return logger

def log_dataframe(df, logger: logging.Logger, name: str = "DataFrame") -> None:

    logger.debug(
        f'''{name} head:\n {df.head()}\n----------\n''')

def log_dataframes(*args, logger: logging.Logger) -> None:

    for gdf in args:

        logger.debug(
            f'''DataFrame head:\n {gdf.head()}\n----------\n''')

Peut utiliser ses fonctions via :

from custom_logging import create_logger, log_dataframe

Ou import custom_logging et custom_logging.create_logger() etc.

Voir également les sections 'Gestionnaires et formateurs multiples' et 'Journalisation vers plusieurs destinations' du livre de recettes de journalisation officiel à l'adresse : https://docs.python.org/3/howto/logging-cookbook.html#logging-cookbook

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X