Come disabilitare e riattivare la registrazione della console in Python?


154

Sto usando il modulo di registrazione di Python e desidero disabilitare la registrazione della console per qualche tempo, ma non funziona.

#!/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")

Il codice sopra mostra lo bla blastdout on e non so come posso disabilitare in sicurezza il gestore della console. Come posso essere sicuro di rimuovere temporaneamente la console StreamHandler e non un'altra?


Per chi si chiede perché qualcuno vorrebbe disabilitare la registrazione: non si vorrebbe registrare dati privati ​​come password o chiavi API.
Stevoisiak,

4
@StevenVascellaro. Perché in primo luogo vengono inviati a un logger? Non suona bene ...
Fisico pazzo,

1
@MadPhysicist Ho un'applicazione che invia richieste XML a un'API esterna. Per impostazione predefinita, queste richieste sono registrate in un file. Tuttavia, il login iniziale richiede l'autenticazione con un nome utente e una password, che non voglio che siano registrati.
Stevoisiak,

@StevenVascellaro. Vedo. Grazie per la spiegazione.
Fisico pazzo,

Non mostri come / dove aggiungere i gestori. Se fossero aggiunti al logger di root, ciò impedirebbe alla registrazione di aggiungere StreamHandler predefinito come descritto in docs.python.org/3/library/logging.html#logging.basicConfig Inoltre, per descrizione collegata, StreamHandler predefinito viene aggiunto solo durante la prima chiama il messaggio di registro che emette, quindi quando lo stampi logger.handlersdovrebbe essere vuoto (come precede la logger.debug()chiamata). Viene visualizzato solo il codice in questione [](elenco vuoto di gestori). Verificato con Python 2.7.15 e Python 3.6.6.
Piotr Dobrogost,

Risposte:


197

Ho trovato una soluzione per questo:

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

Ciò impedirà che la registrazione venga inviata al logger superiore che include la registrazione della console.


8
Non credo sia una buona soluzione. Non propagarsi a logger più alti potrebbe avere altre conseguenze indesiderabili.
LFK

2
Se si desidera filtrare il messaggio solo al di sotto di un determinato livello di registro (ad esempio, tutti i INFOmessaggi), è possibile modificare la seconda riga in qualcosa del tipologger.setLevel(logging.WARNING)
Hartley Brody,

2
Come riattiveresti il ​​registro in seguito?
Stevoisiak,

4
Non una risposta come il blocco di propagazione disattiva in modo efficace tutti i gestori del logger principale e la questione chiaramente stati (...) ma può avere altri gestori lì che voglio mantenere il che suggerisce l'intenzione è quella di disabilitare StreamHandler default del logger principale solo .
Piotr Dobrogost,

L'arresto della propagazione dei messaggi non è sufficiente. A partire da Python 3.2 , il logging.lastResortgestore registrerà comunque i messaggi di gravità logging.WARNINGe maggiore sys.stderrin assenza di altri gestori. Vedere la mia risposta .
Maggyero,

106

Io uso:

logger = logging.getLogger()
logger.disabled = True
... whatever you want ...
logger.disabled = False

9
questo funziona anche a logginglivello di modulo per disabilitare completamente la registrazione , ad esempio import logging; logging.disable(logging.CRITICAL);:: docs.python.org/2/library/logging.html#logging.disable
lsh

1
È molto meglio che disabilitare la propagazione.
Mátray Márk,

6
Non una risposta : la domanda chiede come disabilitare solo StreamHandler predefinito .
Piotr Dobrogost,

1
L' disabledattributo non fa parte dell'API pubblica. Vedi bugs.python.org/issue36318 .
Maggyero,

69

Puoi usare:

logging.basicConfig(level=your_level)

dove your_level è uno di quelli:

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

Quindi, se si imposta your_level a logging.CRITICAL , si otterrà solo messaggi critici inviati da:

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

Impostazione your_level per logging.DEBUG mostrerà tutti i livelli di registrazione.

Per maggiori dettagli, dai un'occhiata agli esempi di registrazione.

Allo stesso modo per cambiare livello per ciascun gestore utilizzare la funzione 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)

6
Si tratta in genere di informazioni utili, ma alla domanda è stato chiesto come disabilitare la registrazione della console, non come aggiungere un gestore aggiuntivo. se dovessi esaminare my_logger.handlers con il codice sopra riportato applicato all'esempio originale, vedresti due gestori: il tuo nuovo gestore di file e il gestore di flusso originale.
Joe,

CRITICO era la parola che cercavo. Grazie.
Nishant,

Mi piacerebbe vedere un livello di debug di OFF. È inequivocabile e semplice.
Non una macchina il

46

(domanda da tempo morto, ma per futuri ricercatori)

Più vicino al codice / alle intenzioni del poster originale, questo funziona per me con 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")

Il problema che ho dovuto risolvere è stato rimuovere il gestore stdout dopo averne aggiunto uno nuovo; il codice del logger sembra aggiungere nuovamente automaticamente lo stdout se non sono presenti gestori.


2
La sequenza logger = logging.getLogger(); lhStdout = logger.handlers[0]è errata poiché inizialmente il logger di root non ha gestori - python -c "import logging; assert not logging.getLogger().handlers". Verificato con Python 2.7.15 e Python 3.6.6.
Piotr Dobrogost,

42

Gestore del contesto

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

Esempio di utilizzo:

with DisableLogger():
    do_something()

Mi piace molto questo idioma, ma preferirei poter disabilitare un determinato spazio dei nomi. Ad esempio, voglio solo disabilitare temporaneamente il logger di root. Sebbene utilizzi questo idioma, dovremmo essere in grado di aggiungere / rimuovere temporaneamente solo i gestori e simili.
Chris,

1
La domanda chiede come disabilitare solo StreamHandler predefinito .
Piotr Dobrogost,

1
Non è necessario creare la tua classe, puoi usare @contextmanager da contextlib e scrivere una funzione cedevole
KristianR

Se ti piacciono i frutti esotici sulla tua pizza. Sicuro.
user3504575,

34

Per disabilitare completamente la registrazione :

logging.disable(sys.maxint) # Python 2

logging.disable(sys.maxsize) # Python 3

Per abilitare la registrazione :

logging.disable(logging.NOTSET)

Altre risposte forniscono soluzioni alternative che non risolvono completamente il problema, ad esempio

logging.getLogger().disabled = True

e, per alcuni nmaggiori di 50,

logging.disable(n)

Il problema con la prima soluzione è che funziona solo per il logger root. Altri logger creati usando, diciamo, logging.getLogger(__name__)non sono disabilitati da questo metodo.

La seconda soluzione ha effetto su tutti i registri. Ma limita l'output a livelli superiori a quello indicato, quindi è possibile sovrascriverlo registrando con un livello superiore a 50.

Ciò può essere prevenuto da

logging.disable(sys.maxint)

che per quanto posso dire (dopo aver esaminato la fonte ) è l'unico modo per disabilitare completamente la registrazione.


1
Sottovaluta la domanda: come disabilitare solo
Piotr Dobrogost,

27

Ci sono alcune risposte davvero belle qui, ma a quanto pare la più semplice non è presa troppo in considerazione (solo da infinito).

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

Questo disabilita il logger di root, e quindi tutti gli altri logger. Non ho ancora testato, ma dovrebbe essere anche il più veloce.

Dal codice di registrazione in Python 2.7 vedo questo

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)

Ciò significa che quando è disabilitato non viene chiamato nessun gestore, e dovrebbe essere più efficiente il filtraggio su un valore molto alto o l'impostazione di un gestore no-op, ad esempio.


1
A meno che non log = logging.getLogger(__name__)
stia

2
Questo potrebbe essere problematico se hai a che fare con più logger o più gestori. Se, ad esempio, vuoi comunque accedere a un file ma vuoi disabilitare il gestore di flusso in un caso specifico.
Joe,

1
Questo disabilita il logger di root, e quindi tutti gli altri logger - disabilitando in senso stretto il logger di root non disabilita nessun altro logger. Inoltre la domanda pone sulla disabilitazione del solo StreamHandler predefinito .
Piotr Dobrogost,

L' disabledattributo non fa parte dell'API pubblica. Vedi bugs.python.org/issue36318 .
Maggyero,

10

Non è necessario deviare stdout. Ecco il modo migliore per farlo:

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

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

Un modo ancora più semplice è:

logging.getLogger().setLevel(100)

4
In Python 2.7+ questo è disponibile come NullHandler ()
Pierre,

1
Il motivo per cui funziona (disabilita StreamHandler predefinito) può essere visto quando si legge la descrizione della logging.basicConfig()funzione (l'enfasi è mia): esegue la configurazione di base per il sistema di registrazione creando uno StreamHandler con un formatter predefinito e aggiungendolo al logger root. Le funzioni debug (), info (), warning (), error () e critical () chiameranno automaticamente basicConfig () se non vengono definiti gestori per il logger root . - docs.python.org/3/library/logging.html#logging.basicConfig
Piotr Dobrogost

2

Non conosco molto bene il modulo di registrazione, ma lo sto usando nel modo in cui di solito desidero disabilitare solo i messaggi di debug (o informazioni). Puoi usareHandler.setLevel() per impostare il livello di registrazione su CRITICO o superiore.

Inoltre, è possibile sostituire sys.stderr e sys.stdout con un file aperto per la scrittura. Vedi http://docs.python.org/library/sys.html#sys. stdout . Ma non lo consiglierei.


Questo potrebbe funzionare se logger.handlers dovesse contenere qualcosa, attualmente lo è [].
sorin,

2

Potresti anche:

handlers = app.logger.handlers
# detach console handler
app.logger.handlers = []
# attach
app.logger.handlers = handlers

Perché stai usando app.loggerciò che non specifichi nemmeno al posto del root logger esplicitamente menzionato nella domanda ( logging.getLogger()) e nella maggior parte delle risposte? Come fai a sapere che puoi modificare in sicurezza la handlersproprietà invece di chiamare il Logger.addHandlermetodo?
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')

Uscita 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

contenuto del file 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

2
Aggiungi una descrizione del codice. Sarebbe molto meglio
Mathews Sunny,

2

Modificando un livello in "logging.config.dictConfig" sarai in grado di portare l'intero livello di registrazione a un nuovo livello.

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,
    },
}

})


1

Ho trovato una soluzione elegante usando decoratoriHai , che affronta il seguente problema: cosa succede se stai scrivendo un modulo con diverse funzioni, ognuna con diversi messaggi di debug e vuoi disabilitare il login in tutte le funzioni tranne quella su cui ti stai attualmente concentrando?

Puoi farlo usando i decoratori:

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

Quindi, puoi fare:

@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")
    ...

Anche se chiami function_already_debuggeddall'interno function_being_focused, esegui il debug dei messaggi dafunction_already_debugged non verranno mostrati. Ciò garantisce che vedrai solo i messaggi di debug dalla funzione su cui ti stai concentrando.

Spero che sia d'aiuto!


0

Se si desidera disabilitare temporaneamente un determinato logger, ecco cosa si fa.

Registro di esempio

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

Codice

django_request_logger = logging.getLogger('django.request')
django_request_logger.disabled = True
django_request_logger.disabled = False

0

Nella libreria Logging Python, è possibile disabilitare completamente la registrazione (per tutti i livelli) per un particolare logger effettuando una delle seguenti operazioni:

  1. Aggiunta al logger di un logging.NullHandler()gestore (per impedire al logging.lastResortgestore di registrare eventi di gravità logging.WARNINGe maggiori su sys.stderr) e impostare l' propagateattributo di quel logger suFalse (per impedire al logger di passare gli eventi ai gestori dei suoi logger antenati).

    • Utilizzando l'API principale:

      import logging
      
      logging.getLogger("foo").addHandler(logging.NullHandler())
      logging.getLogger("foo").propagate = False
    • Utilizzando l'API di configurazione:

      import logging.config
      
      logging.config.dictConfig({
          "version": 1,
          "handlers": {
              "null": {
                  "class": "logging.NullHandler"
              }
          },
          "loggers": {
              "foo": {
                  "handlers": ["null"],
                  "propagate": False
              }
          }
      })
  2. Aggiunta al logger di un lambda record: Falsefiltro.

    • Utilizzando l'API principale:

      import logging
      
      logging.getLogger("foo").addFilter(lambda record: False)
    • Utilizzando l'API di configurazione:

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

Avvertimento. - Contrariamente alla prima soluzione, la seconda soluzione non disabilita la registrazione dai logger figlio (ad esempio logging.getLogger("foo.bar")), quindi si dovrebbe usarla solo per disabilitare la registrazione per un singolo logger.

Nota. - L'impostazione disableddell'attributo del logger su Truenon è una terza soluzione, in quanto non fa parte dell'API pubblica. Vedi https://bugs.python.org/issue36318 :

import logging

logging.getLogger("foo").disabled = True  # DO NOT DO THAT

-1

sottoclasse il gestore che si desidera disabilitare temporaneamente:

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)

trovare il gestore per nome è abbastanza semplice:

_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))

una volta trovato:

_handler.disable()
doStuff()
_handler.enable()
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.