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