¿Cómo deshabilitar y volver a habilitar el registro de la consola en Python?

154

Estoy usando el módulo de registro de Python y quiero deshabilitar el registro de la consola durante algún tiempo, pero no funciona.

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

El código anterior muestra el bla blastdout activado y no sé cómo puedo desactivar de forma segura el controlador de la consola. ¿Cómo puedo estar seguro de que elimino temporalmente la consola StreamHandler y no otra?

Sorin
fuente
Para aquellos que se preguntan por qué alguien querría deshabilitar el registro: no querría registrar datos privados como contraseñas o claves API.
Stevoisiak
44
@StevenVascellaro. ¿Por qué se envían a un registrador en primer lugar entonces? Eso no suena bien ...
Mad Physicist
1
@MadPhysicist Tengo una aplicación que envía solicitudes XML a una API externa. Por defecto, estas solicitudes se registran en un archivo. Sin embargo, el inicio de sesión inicial requiere autenticación con un nombre de usuario y contraseña, que no quiero registrar.
Stevoisiak
@StevenVascellaro. Veo. Gracias por la explicación.
Físico loco
No muestra cómo / dónde agrega sus controladores. Si se agregaron al registrador raíz, esto evitaría que el registro agregue StreamHandler predeterminado como se describe en docs.python.org/3/library/logging.html#logging.basicConfig Además, según la descripción vinculada, el StreamHandler predeterminado solo se agrega durante la primera vez mensaje de registro de emisión de llamadas, por lo que cuando imprima logger.handlersdebe estar vacío (como precede a la logger.debug()llamada). El código en cuestión solo se muestra [](lista vacía de controladores). Verificado con Python 2.7.15 y Python 3.6.6.
Piotr Dobrogost

Respuestas:

197

Encontré una solución para esto:

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

Esto evitará que el registro se envíe al registrador superior que incluye el registro de la consola.

Sorin
fuente
8
No creo que esta sea una buena solución. No propagarse a madereros superiores podría tener otras consecuencias indeseables.
lfk
2
Si solo desea filtrar el mensaje por debajo de un cierto nivel de registro (por ejemplo, todos los INFOmensajes), puede cambiar la segunda línea a algo así comologger.setLevel(logging.WARNING)
Hartley Brody,
2
¿Cómo volvería a habilitar el registro después?
Stevoisiak
44
No es una respuesta, ya que el bloqueo de la propagación deshabilita efectivamente todos los controladores del registrador raíz y la pregunta indica claramente (...) pero es posible que tenga otros controladores allí que quiero mantener, lo que sugiere que la intención es desactivar StreamHandler predeterminado del registrador raíz solamente .
Piotr Dobrogost
Detener la propagación de mensajes no es suficiente. Desde Python 3.2 , el logging.lastResortcontrolador seguirá registrando mensajes de gravedad logging.WARNINGy mayor sys.stderren ausencia de otros controladores. Mira mi respuesta .
Maggyero
106

Yo suelo:

logger = logging.getLogger()
logger.disabled = True
... whatever you want ...
logger.disabled = False
infinito
fuente
9
esto también funciona a loggingnivel de módulo para deshabilitar el registro por completo , por ejemplo import logging; logging.disable(logging.CRITICAL);:: docs.python.org/2/library/logging.html#logging.disable
lsh
1
Esto es mucho mejor que deshabilitar la propagación.
Mátray Márk
66
No es una respuesta : la pregunta pregunta cómo deshabilitar solo StreamHandler predeterminado .
Piotr Dobrogost
1
El disabledatributo no es parte de la API pública. Ver bugs.python.org/issue36318 .
Maggyero
69

Puedes usar:

logging.basicConfig(level=your_level)

donde your_level es uno de esos:

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

Entonces, si configura your_level en logging.CRITICAL , solo recibirá mensajes críticos enviados por:

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

Establecer your_level en logging.DEBUG mostrará todos los niveles de registro.

Para obtener más detalles, eche un vistazo a los ejemplos de registro.

De la misma manera, para cambiar el nivel de cada controlador, use la función 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
fuente
66
Generalmente, esta es información útil, pero la pregunta es cómo deshabilitar el registro de la consola, no cómo agregar un controlador adicional. si examinara my_logger.handlers con el código anterior aplicado al ejemplo original, vería dos controladores: su nuevo controlador de archivos y el controlador de secuencia original.
Joe
CRÍTICA era la palabra que estaba buscando. Gracias.
Nishant
Me encantaría ver un nivel de depuración de OFF. Es inequívoco y simple.
No es una máquina
46

(pregunta largamente muerta, pero para futuros buscadores)

Más cerca del código / intención del póster original, esto funciona para mí en 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")

El problema que tuve que resolver fue eliminar el controlador estándar después de agregar uno nuevo; el código del registrador parece volver a agregar automáticamente el stdout si no hay controladores disponibles.

l82ky
fuente
2
La secuencia logger = logging.getLogger(); lhStdout = logger.handlers[0]es incorrecta ya que el registrador raíz inicialmente no tiene controladores - python -c "import logging; assert not logging.getLogger().handlers". Verificado con Python 2.7.15 y Python 3.6.6.
Piotr Dobrogost
42

Gestor de contexto

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

Ejemplo de uso:

with DisableLogger():
    do_something()
pymen
fuente
Realmente me gusta este idioma, pero preferiría poder desactivar un espacio de nombres en particular. Por ejemplo, solo quiero que el registrador raíz esté deshabilitado temporalmente. Si bien utilizamos este modismo, deberíamos poder agregar / eliminar temporalmente controladores y demás.
Chris
1
La pregunta pregunta cómo deshabilitar solo StreamHandler predeterminado .
Piotr Dobrogost
1
No necesita rodar su propia clase, puede usar @contextmanager de contextlib y escribir una función de rendimiento
KristianR
Si te gustan las frutas exóticas en tu pizza. Por supuesto.
user3504575
34

Para deshabilitar completamente el registro :

logging.disable(sys.maxint) # Python 2

logging.disable(sys.maxsize) # Python 3

Para habilitar el registro :

logging.disable(logging.NOTSET)

Otras respuestas proporcionan soluciones alternativas que no resuelven completamente el problema, como

logging.getLogger().disabled = True

y, para algunos nmayores de 50,

logging.disable(n)

El problema con la primera solución es que solo funciona para el registrador raíz. Otros registradores creados usando, por ejemplo, logging.getLogger(__name__)no están deshabilitados por este método.

La segunda solución afecta a todos los registros. Pero limita la salida a niveles superiores a los dados, por lo que uno podría anularlo iniciando sesión con un nivel superior a 50.

Eso puede ser prevenido por

logging.disable(sys.maxint)

que por lo que puedo decir (después de revisar la fuente ) es la única forma de deshabilitar completamente el registro.

starfry
fuente
1
Voto negativo ya que la pregunta pregunta cómo deshabilitar solo
Piotr Dobrogost
27

Aquí hay algunas respuestas realmente buenas, pero aparentemente la más simple no se toma demasiado en consideración (solo desde infinito).

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

Esto desactiva el registrador raíz y, por lo tanto, todos los demás registradores. Realmente no lo he probado, pero también debería ser el más rápido.

Del código de registro en Python 2.7 veo esto

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)

Lo que significa que cuando está deshabilitado no se llama a ningún controlador, y debería ser más eficiente que filtrar a un valor muy alto o establecer un controlador no operativo, por ejemplo.

andrea_crotti
fuente
1
A menos que esté haciendo algo mal, esto solo deshabilita el registrador raíz y no ninguno creado comolog = logging.getLogger(__name__)
starfry
2
Esto podría ser problemático si se trata de múltiples registradores o manejadores múltiples. Si, por ejemplo, aún desea iniciar sesión en un archivo pero desea deshabilitar el controlador de flujo en un caso específico.
Joe
1
Esto deshabilita el registrador raíz y, por lo tanto, todos los demás registradores ; hablando estrictamente, deshabilitar el registrador raíz no deshabilita ningún otro registrador. Además, la pregunta se refiere a deshabilitar solo StreamHandler predeterminado .
Piotr Dobrogost
El disabledatributo no es parte de la API pública. Ver bugs.python.org/issue36318 .
Maggyero
10

No es necesario desviar stdout. Aquí hay una mejor manera de hacerlo:

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

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

Una forma aún más simple es:

logging.getLogger().setLevel(100)
Ehsan Foroughi
fuente
44
En Python 2.7+ esto está disponible como NullHandler ()
Pierre
1
La razón por la que esto funciona (deshabilita StreamHandler predeterminado) se puede ver al leer la descripción de la logging.basicConfig()función (énfasis mío): realiza la configuración básica para el sistema de registro al crear un StreamHandler con un formateador predeterminado y agregarlo al registrador raíz. Las funciones debug (), info (), warning (), error () y critical () llamarán a basicConfig () automáticamente si no hay controladores definidos para el registrador raíz . - docs.python.org/3/library/logging.html#logging.basicConfig
Piotr Dobrogost
2

No conozco muy bien el módulo de registro, pero lo estoy usando de la forma en que generalmente quiero deshabilitar solo los mensajes de depuración (o información). Puede usar Handler.setLevel()para establecer el nivel de registro en CRÍTICO o superior.

Además, puede reemplazar sys.stderr y sys.stdout por un archivo abierto para escritura. Ver http://docs.python.org/library/sys.html#sys. la salida estándar . Pero no lo recomendaría.

Cangrejo
fuente
Esto podría funcionar si logger.handlers contuviera algo, actualmente lo es [].
sorin
2

También podrías:

handlers = app.logger.handlers
# detach console handler
app.logger.handlers = []
# attach
app.logger.handlers = handlers
Italo Maia
fuente
¿Por qué está utilizando app.loggerlo que ni siquiera especifica en lugar del registrador raíz mencionado explícitamente en la pregunta ( logging.getLogger()) y la mayoría de las respuestas? ¿Cómo sabe que puede modificar de forma segura la handlerspropiedad en lugar de llamar al Logger.addHandlermétodo?
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')

Salida de consola:

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

contenido del archivo 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
fuente
2
Agregue alguna descripción sobre el código. Ayudaría mucho mejor
Mathews Sunny
2

Al cambiar un nivel en "logging.config.dictConfig", podrá llevar todo el nivel de registro a un nuevo nivel.

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
fuente
1

Encontré una solución elegante usando decoradores , que aborda el siguiente problema: ¿qué sucede si está escribiendo un módulo con varias funciones, cada una de ellas con varios mensajes de depuración, y desea deshabilitar el registro en todas las funciones excepto en la que se está enfocando actualmente?

Puedes hacerlo usando decoradores:

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

Entonces, puedes hacer:

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

Incluso si llama function_already_debuggeddesde dentro function_being_focused, function_already_debuggedno se mostrarán los mensajes de depuración de . Esto garantiza que solo verá los mensajes de depuración de la función en la que se está enfocando.

¡Espero eso ayude!

Carlos Souza
fuente
0

Si desea deshabilitar un determinado registrador temporalmente, esto es lo que hizo.

Registro de ejemplo

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

Código

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

En la biblioteca Logging Python, se puede deshabilitar por completo el registro (para todos los niveles) para un registrador en particular haciendo lo siguiente:

  1. Agregar al registrador un logging.NullHandler()controlador (para evitar que el logging.lastResortcontrolador registre eventos de gravedad logging.WARNINGy mayor a sys.stderr) y establecer el propagateatributo de ese registrador enFalse (para evitar que el registrador pase los eventos a los controladores de sus registradores ancestrales).

    • Usando la API principal:

      import logging
      
      logging.getLogger("foo").addHandler(logging.NullHandler())
      logging.getLogger("foo").propagate = False
    • Usando la API de configuración:

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

    • Usando la API principal:

      import logging
      
      logging.getLogger("foo").addFilter(lambda record: False)
    • Usando la API de configuración:

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

Advertencia. - Contrariamente a la primera solución, la segunda solución no desactiva el registro de los registradores secundarios (por ejemplo logging.getLogger("foo.bar")), por lo que se debe usar solo para desactivar el registro de un único registrador.

Nota. - Establecer el disabledatributo del registrador en Trueno es una tercera solución, ya que no es parte de la API pública. Ver https://bugs.python.org/issue36318 :

import logging

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

Subclase el controlador que desea desactivar temporalmente:

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)

encontrar el controlador por nombre es bastante fácil:

_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 vez encontrado:

_handler.disable()
doStuff()
_handler.enable()
jake77
fuente