Comment désactiver et réactiver la journalisation de la console en Python?

154

J'utilise le module de journalisation de Python et je souhaite désactiver la journalisation de la console pendant un certain temps, mais cela ne fonctionne pas.

#!/usr/bin/python
import logging

logger = logging.getLogger() # this gets the root logger
# ... here I add my own handlers 
#logger.removeHandler(sys.stdout)
#logger.removeHandler(sys.stderr)

print logger.handlers 
# this will print [<logging.StreamHandler instance at ...>]
# but I may have other handlers there that I want to keep

logger.debug("bla bla")

Le code ci-dessus affiche le bla blasur stdout et je ne sais pas comment puis-je désactiver en toute sécurité le gestionnaire de console. Comment puis-je être sûr de supprimer temporairement la console StreamHandler et pas une autre?

Sorin
la source
Pour ceux qui se demandent pourquoi quelqu'un voudrait désactiver la journalisation: vous ne voudriez pas enregistrer des données privées telles que des mots de passe ou des clés API.
Stevoisiak
4
@StevenVascellaro. Pourquoi sont-ils donc envoyés à un enregistreur? Cela ne semble pas juste ...
Mad Physicist
1
@MadPhysicist J'ai une application qui envoie des requêtes XML à une API externe. Par défaut, ces demandes sont enregistrées dans un fichier. Cependant, la connexion initiale nécessite une authentification avec un nom d'utilisateur et un mot de passe, que je ne souhaite pas enregistrer.
Stevoisiak
@StevenVascellaro. Je vois. Merci pour l'explication.
Mad Physicist
Vous ne montrez pas comment / où vous ajoutez vos gestionnaires. S'ils étaient ajoutés à l'enregistreur racine, cela empêcherait la journalisation d'ajouter StreamHandler par défaut, comme décrit à docs.python.org/3/library/logging.html#logging.basicConfig De plus, par description liée, le StreamHandler par défaut n'est ajouté que pendant la première appelez le message du journal d'émission, donc lorsque vous imprimez, logger.handlersil doit être vide (car il précède l' logger.debug()appel). Le code en question s'affiche uniquement [](liste vide de gestionnaires). Vérifié avec Python 2.7.15 et Python 3.6.6.
Piotr Dobrogost

Réponses:

197

J'ai trouvé une solution pour cela:

logger = logging.getLogger('my-logger')
logger.propagate = False
# now if you use logger it will not log to console.

Cela empêchera la journalisation d'être envoyée à l'enregistreur supérieur qui inclut la journalisation de la console.

Sorin
la source
8
Je ne pense pas que ce soit une bonne solution. Ne pas se propager aux bûcherons supérieurs pourrait avoir d'autres conséquences indésirables.
lfk
2
Si vous souhaitez filtrer uniquement les messages en dessous d'un certain niveau de journalisation (par exemple, tous les INFOmessages), vous pouvez modifier la deuxième ligne en quelque chose commelogger.setLevel(logging.WARNING)
Hartley Brody
2
Comment réactiveriez-vous le journal par la suite?
Stevoisiak
4
Ce n'est pas une réponse car le blocage de la propagation désactive effectivement tous les gestionnaires de l'enregistreur racine et la question indique clairement (…) mais je peux avoir d'autres gestionnaires là-bas que je veux garder, ce qui suggère que l'intention est de désactiver le StreamHandler par défaut de l'enregistreur racine uniquement .
Piotr Dobrogost
Arrêter la propagation des messages ne suffit pas. Depuis Python 3.2 , le logging.lastResortgestionnaire enregistrera toujours les messages de gravité logging.WARNINGet supérieure à sys.stderren l'absence d'autres gestionnaires. Voyez ma réponse .
Maggyero
106

J'utilise:

logger = logging.getLogger()
logger.disabled = True
... whatever you want ...
logger.disabled = False
infinito
la source
9
cela fonctionne également au loggingniveau du module pour désactiver complètement la journalisation , par exemple import logging; logging.disable(logging.CRITICAL);:: docs.python.org/2/library/logging.html#logging.disable
lsh
1
C'est bien mieux que de désactiver la propagation.
Mátray Márk
6
Pas de réponse - la question demande comment désactiver uniquement StreamHandler par défaut .
Piotr Dobrogost
1
L' disabledattribut ne fait pas partie de l'API publique. Voir bugs.python.org/issue36318 .
Maggyero
69

Vous pouvez utiliser:

logging.basicConfig(level=your_level)

your_level est l'un de ceux-ci:

      'debug': logging.DEBUG,
      'info': logging.INFO,
      'warning': logging.WARNING,
      'error': logging.ERROR,
      'critical': logging.CRITICAL

Donc, si vous définissez your_level sur logging.CRITICAL , vous ne recevrez que les messages critiques envoyés par:

logging.critical('This is a critical error message')

Définition de your_level sur logging.DEBUG affichera tous les niveaux de journalisation.

Pour plus de détails, veuillez consulter les exemples de journalisation.

De la même manière, pour modifier le niveau de chaque gestionnaire, utilisez la fonction Handler.setLevel () .

import logging
import logging.handlers

LOG_FILENAME = '/tmp/logging_rotatingfile_example.out'

# Set up a specific logger with our desired output level
my_logger = logging.getLogger('MyLogger')
my_logger.setLevel(logging.DEBUG)

# Add the log message handler to the logger
handler = logging.handlers.RotatingFileHandler(
          LOG_FILENAME, maxBytes=20, backupCount=5)

handler.setLevel(logging.CRITICAL)

my_logger.addHandler(handler)
Vadikus
la source
6
Ce sont généralement des informations utiles, mais la question demandait comment désactiver la journalisation de la console, pas comment ajouter un gestionnaire supplémentaire. si vous examiniez my_logger.handlers avec le code ci-dessus appliqué à l'exemple d'origine, vous verriez deux gestionnaires - votre nouveau gestionnaire de fichiers et le gestionnaire de flux d'origine.
Joe le
CRITIQUE était le mot que je cherchais. Merci.
Nishant
J'aimerais voir un niveau de débogage OFF. C'est simple et sans ambiguïté.
Not a machine
46

(question morte depuis longtemps, mais pour les futurs chercheurs)

Plus proche du code / de l'intention de l'affiche originale, cela fonctionne pour moi sous python 2.6

#!/usr/bin/python
import logging

logger = logging.getLogger() # this gets the root logger

lhStdout = logger.handlers[0]  # stdout is the only handler initially

# ... here I add my own handlers 
f = open("/tmp/debug","w")          # example handler
lh = logging.StreamHandler(f)
logger.addHandler(lh)

logger.removeHandler(lhStdout)

logger.debug("bla bla")

Le piège que j'ai dû résoudre était de supprimer le gestionnaire stdout après en avoir ajouté un nouveau; le code de l'enregistreur semble ré-ajouter automatiquement la sortie standard si aucun gestionnaire n'est présent.

l82ky
la source
2
La séquence logger = logging.getLogger(); lhStdout = logger.handlers[0]est erronée car l'enregistreur racine n'a initialement aucun gestionnaire - python -c "import logging; assert not logging.getLogger().handlers". Vérifié avec Python 2.7.15 et Python 3.6.6.
Piotr Dobrogost
42

Gestionnaire de contexte

import logging 
class DisableLogger():
    def __enter__(self):
       logging.disable(logging.CRITICAL)
    def __exit__(self, a, b, c):
       logging.disable(logging.NOTSET)

Exemple d'utilisation:

with DisableLogger():
    do_something()
pymen
la source
J'aime vraiment cet idiome, mais je préférerais pouvoir désactiver un espace de noms particulier. Par exemple, je veux juste que l'enregistreur racine soit temporairement désactivé. Bien qu'en utilisant cet idiome, nous devrions être en mesure d'ajouter / supprimer temporairement des gestionnaires et autres.
Chris
1
La question demande comment désactiver uniquement StreamHandler par défaut .
Piotr Dobrogost
1
Vous n'avez pas besoin de lancer votre propre classe, vous pouvez utiliser @contextmanager de contextlib et écrire une fonction de rendement
KristianR
Si vous aimez les fruits exotiques sur votre pizza. Sûr.
user3504575
34

Pour désactiver complètement la journalisation :

logging.disable(sys.maxint) # Python 2

logging.disable(sys.maxsize) # Python 3

Pour activer la journalisation :

logging.disable(logging.NOTSET)

D'autres réponses fournissent des solutions de contournement qui ne résolvent pas complètement le problème, telles que

logging.getLogger().disabled = True

et, pour certains nsupérieurs à 50,

logging.disable(n)

Le problème avec la première solution est qu'elle ne fonctionne que pour l'enregistreur racine. Les autres enregistreurs créés à l'aide, par exemple, logging.getLogger(__name__)ne sont pas désactivés par cette méthode.

La deuxième solution affecte tous les journaux. Mais il limite la sortie à des niveaux supérieurs à ce qui est donné, donc on peut la remplacer en enregistrant avec un niveau supérieur à 50.

Cela peut être évité par

logging.disable(sys.maxint)

qui pour autant que je sache (après avoir examiné la source ) est le seul moyen de désactiver complètement la journalisation.

starfry
la source
1
Downvote car la question demande comment désactiver StreamHandler standard uniquement
Piotr Dobrogost
27

Il y a de très belles réponses ici, mais apparemment la plus simple n'est pas trop prise en compte (uniquement à partir d'infinito).

root_logger = logging.getLogger()
root_logger.disabled = True

Cela désactive l'enregistreur racine, et donc tous les autres enregistreurs. Je n'ai pas vraiment testé mais ça devrait être aussi le plus rapide.

À partir du code de journalisation en python 2.7, je vois ceci

def handle(self, record):
    """
    Call the handlers for the specified record.

    This method is used for unpickled records received from a socket, as
    well as those created locally. Logger-level filtering is applied.
    """
    if (not self.disabled) and self.filter(record):
        self.callHandlers(record)

Ce qui signifie que lorsqu'il est désactivé, aucun gestionnaire n'est appelé, et il devrait être plus efficace que le filtrage à une valeur très élevée ou la définition d'un gestionnaire no-op par exemple.

andrea_crotti
la source
1
À moins que je ne fasse quelque chose de mal, cela ne désactive que l'enregistreur racine et non tout créé commelog = logging.getLogger(__name__)
starfry
2
Cela peut être problématique si vous avez affaire à plusieurs enregistreurs ou à plusieurs gestionnaires. Si, par exemple, vous souhaitez toujours vous connecter à un fichier mais que vous souhaitez désactiver le gestionnaire de flux dans un cas spécifique.
Joe le
1
Cela désactive l'enregistreur racine, et donc tous les autres enregistreurs - à proprement parler, la désactivation de l'enregistreur racine ne désactive aucun autre enregistreur. En outre, la question concerne la désactivation de StreamHandler par défaut uniquement .
Piotr Dobrogost
L' disabledattribut ne fait pas partie de l'API publique. Voir bugs.python.org/issue36318 .
Maggyero
10

Pas besoin de détourner stdout. Voici une meilleure façon de le faire:

import logging
class MyLogHandler(logging.Handler):
    def emit(self, record):
        pass

logging.getLogger().addHandler(MyLogHandler())

Un moyen encore plus simple est:

logging.getLogger().setLevel(100)
Ehsan Foroughi
la source
4
Dans Python 2.7+, ceci est disponible en tant que NullHandler ()
Pierre
1
La raison pour laquelle cela fonctionne (désactive StreamHandler par défaut) peut être vue lors de la lecture de la description de la logging.basicConfig()fonction (c'est moi qui souligne): Effectue une configuration de base pour le système de journalisation en créant un StreamHandler avec un formateur par défaut et en l'ajoutant à l'enregistreur racine. Les fonctions debug (), info (), warning (), error () et critical () appelleront automatiquement basicConfig () si aucun gestionnaire n'est défini pour le journal racine . - docs.python.org/3/library/logging.html#logging.basicConfig
Piotr Dobrogost
2

Je ne connais pas très bien le module de journalisation, mais je l'utilise de la manière dont je souhaite généralement désactiver uniquement les messages de débogage (ou d'informations). Vous pouvez utiliserHandler.setLevel() pour définir le niveau de journalisation sur CRITICAL ou supérieur.

De plus, vous pouvez remplacer sys.stderr et sys.stdout par un fichier ouvert en écriture. Voir http://docs.python.org/library/sys.html#sys. stdout . Mais je ne recommanderais pas cela.

Krab
la source
Cela pourrait fonctionner si logger.handlers contenait quelque chose, il l'est actuellement [].
sorin
2

Vous pouvez également:

handlers = app.logger.handlers
# detach console handler
app.logger.handlers = []
# attach
app.logger.handlers = handlers
Italo Maia
la source
Pourquoi utilisez-vous app.logger ce que vous ne spécifiez même pas à la place de l'enregistreur racine explicitement mentionné dans la question ( logging.getLogger()) et la plupart des réponses? Comment savez-vous que vous pouvez modifier en toute sécurité la handlerspropriété au lieu d'appeler la Logger.addHandlerméthode?
Piotr Dobrogost
2
import logging

log_file = 'test.log'
info_format = '%(asctime)s - %(levelname)s - %(message)s'
logging.config.dictConfig({
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'info_format': {
            'format': info_format
        },
    },
    'handlers': {
        'console': {
            'level': 'INFO',
            'class': 'logging.StreamHandler',
            'formatter': 'info_format'
        },
        'info_log_file': {
            'class': 'logging.handlers.RotatingFileHandler',
            'level': 'INFO',
            'filename': log_file,
            'formatter': 'info_format'
        }
    },
    'loggers': {
        '': {
            'handlers': [
                'console',
                'info_log_file'
            ],
            'level': 'INFO'
        }
    }
})


class A:

    def __init__(self):
        logging.info('object created of class A')

        self.logger = logging.getLogger()
        self.console_handler = None

    def say(self, word):
        logging.info('A object says: {}'.format(word))

    def disable_console_log(self):
        if self.console_handler is not None:
            # Console log has already been disabled
            return

        for handler in self.logger.handlers:
            if type(handler) is logging.StreamHandler:
                self.console_handler = handler
                self.logger.removeHandler(handler)

    def enable_console_log(self):
        if self.console_handler is None:
            # Console log has already been enabled
            return

        self.logger.addHandler(self.console_handler)
        self.console_handler = None


if __name__ == '__main__':
    a = A()
    a.say('111')
    a.disable_console_log()
    a.say('222')
    a.enable_console_log()
    a.say('333')

Sortie de la console:

2018-09-15 15:22:23,354 - INFO - object created of class A
2018-09-15 15:22:23,356 - INFO - A object says: 111
2018-09-15 15:22:23,358 - INFO - A object says: 333

Contenu du fichier test.log:

2018-09-15 15:22:23,354 - INFO - object created of class A
2018-09-15 15:22:23,356 - INFO - A object says: 111
2018-09-15 15:22:23,357 - INFO - A object says: 222
2018-09-15 15:22:23,358 - INFO - A object says: 333
Shawn Hu
la source
2
Ajoutez une description du code. Cela aiderait beaucoup mieux
Mathews Sunny
2

En changeant un niveau dans le "logging.config.dictConfig", vous serez en mesure de porter tout le niveau de journalisation à un nouveau niveau.

logging.config.dictConfig({
'version': 1,
'disable_existing_loggers': False,
'formatters': {
    'console': {
        'format': '%(name)-12s %(levelname)-8s %(message)s'
    },
    'file': {
        'format': '%(asctime)s %(name)-12s %(levelname)-8s %(message)s'
    }
},
'handlers': {
    'console': {
        'class': 'logging.StreamHandler',
        'formatter': 'console'
    },
#CHANGE below level from DEBUG to THE_LEVEL_YOU_WANT_TO_SWITCH_FOR
#if we jump from DEBUG to INFO
# we won't be able to see the DEBUG logs in our logging.log file
    'file': {
        'level': 'DEBUG',
        'class': 'logging.FileHandler',
        'formatter': 'file',
        'filename': 'logging.log'
    },
},
'loggers': {
    '': {
        'level': 'DEBUG',
        'handlers': ['console', 'file'],
        'propagate': False,
    },
}

})

Ganesh Kalidas
la source
1

Trouvé une solution élégante utilisant des décorateurs , qui résout le problème suivant: que se passe-t-il si vous écrivez un module avec plusieurs fonctions, chacune avec plusieurs messages de débogage, et que vous voulez désactiver la journalisation dans toutes les fonctions sauf celle sur laquelle vous vous concentrez actuellement?

Vous pouvez le faire en utilisant des décorateurs:

import logging, sys
logger = logging.getLogger()
logging.basicConfig(stream=sys.stderr, level=logging.DEBUG)


def disable_debug_messages(func):
    def wrapper(*args, **kwargs):
        prev_state = logger.disabled
        logger.disabled = True
        result = func(*args, **kwargs)
        logger.disabled = prev_state
        return result
    return wrapper

Ensuite, vous pouvez faire:

@disable_debug_messages
def function_already_debugged():
    ...
    logger.debug("This message won't be showed because of the decorator")
    ...

def function_being_focused():
    ...
    logger.debug("This message will be showed")
    ...

Même si vous appelez function_already_debuggedde l'intérieur function_being_focused, les messages de débogage de function_already_debuggedne seront pas affichés. Cela garantit que vous ne verrez que les messages de débogage de la fonction sur laquelle vous vous concentrez.

J'espère que ça aide!

Carlos Souza
la source
0

Si vous souhaitez désactiver temporairement un certain enregistreur, voici ce qui est fait.

Exemple de journal

2019-10-02 21:28:45,663 django.request PID: 8  Internal Server Error: /service_portal/get_all_sites

Code

django_request_logger = logging.getLogger('django.request')
django_request_logger.disabled = True
django_request_logger.disabled = False
sonance207
la source
0

Dans la bibliothèque Logging Python, on peut désactiver complètement la journalisation (pour tous les niveaux) pour un enregistreur particulier en effectuant l'une des opérations suivantes:

  1. Ajouter au journal un logging.NullHandler()gestionnaire (pour empêcher le logging.lastResortgestionnaire de consigner les événements de gravité logging.WARNINGet supérieure à sys.stderr) et définir l' propagateattribut de ce journal surFalse (pour empêcher le journal de transmettre les événements aux gestionnaires de ses enregistreurs ancêtres).

    • Utilisation de l'API principale:

      import logging
      
      logging.getLogger("foo").addHandler(logging.NullHandler())
      logging.getLogger("foo").propagate = False
    • En utilisant l'API de configuration:

      import logging.config
      
      logging.config.dictConfig({
          "version": 1,
          "handlers": {
              "null": {
                  "class": "logging.NullHandler"
              }
          },
          "loggers": {
              "foo": {
                  "handlers": ["null"],
                  "propagate": False
              }
          }
      })
  2. Ajout d'un lambda record: Falsefiltre à l'enregistreur .

    • Utilisation de l'API principale:

      import logging
      
      logging.getLogger("foo").addFilter(lambda record: False)
    • En utilisant l'API de configuration:

      import logging.config
      
      logging.config.dictConfig({
          "version": 1,
          "filters": {
              "all": {
                  "()": lambda: (lambda record: False)
              }
          },
          "loggers": {
              "foo": {
                  "filters": ["all"]
              }
          }
      })

Avertissement. - Contrairement à la 1ère solution, la 2ème solution ne désactive pas la journalisation des enregistreurs enfants (par exemple logging.getLogger("foo.bar")), il ne faut donc l'utiliser que pour désactiver la journalisation pour un seul enregistreur.

Remarque. - La définition de l' disabledattribut de l'enregistreur sur Truen'est pas une troisième solution, car elle ne fait pas partie de l'API publique. Voir https://bugs.python.org/issue36318 :

import logging

logging.getLogger("foo").disabled = True  # DO NOT DO THAT
Maggyero
la source
-1

sous-classez le gestionnaire que vous souhaitez pouvoir désactiver temporairement:

class ToggledHandler(logging.StreamHandler):
"""A handler one can turn on and off"""

def __init__(self, args, kwargs):
    super(ToggledHandler, self).__init__(*args, **kwargs)
    self.enabled = True  # enabled by default

def enable(self):
    """enables"""
    self.enabled = True

def disable(self):
    """disables"""
    self.enabled = False

def emit(self, record):
    """emits, if enabled"""
    if self.enabled:
        # this is taken from the super's emit, implement your own
        try:
            msg = self.format(record)
            stream = self.stream
            stream.write(msg)
            stream.write(self.terminator)
            self.flush()
        except Exception:
            self.handleError(record)

trouver le gestionnaire par son nom est assez simple:

_handler = [x for x in logging.getLogger('').handlers if x.name == your_handler_name]
if len(_handler) == 1:
    _handler = _handler[0]
else:
    raise Exception('Expected one handler but found {}'.format(len(_handler))

une fois trouvé:

_handler.disable()
doStuff()
_handler.enable()
jake77
la source