2011-09-07 15 views
11

Soy nuevo en el registro de Python y puedo ver fácilmente cómo es preferible a la solución de cocción casera que he creado.¿Deshabilitar el registro por método/función?

Una pregunta que parece que no puedo encontrar una respuesta a: ¿cómo silenciar los mensajes de registro por método/función?

Mi módulo hipotético contiene una sola función. A medida que desarrollo, el registro de llamadas son de gran ayuda:

logging.basicConfig(level=logging.DEBUG, 
       format=('%(levelname)s: %(funcName)s(): %(message)s')) 
log = logging.getLogger() 

my_func1(): 
    stuff... 
    log.debug("Here's an interesting value: %r" % some_value) 
    log.info("Going great here!") 
    more stuff... 

como yo envuelvo mi trabajo en 'my_func1' y empezar a trabajar en una segunda función, 'my_func2', los mensajes de registro de 'my_func1' empezar a ir de "útil" a "desorden".

¿Existe una declaración mágica de línea única, como 'logging.disabled_in_this_func()' que puedo agregar a la parte superior de 'my_func1' para deshabilitar todas las llamadas de registro dentro de 'my_func1', pero todavía dejo las llamadas de registro en todos otras funciones/métodos sin cambios?

Gracias

Linux, Python 2.7.1

+0

Relacionados: http://stackoverflow.com/questions/879732/logging-with-filters –

Respuesta

8

El truco consiste en crear varios registradores.

Hay varios aspectos en esto.

Primero. No use logging.basicConfig() al comienzo de un módulo. usarlo solamente en el interior del interruptor principal a importar

if __name__ == "__main__": 
    logging.basicConfig(...) 
    main() 
    logging.shutdown() 

Segundo. Nunca obtenga el registrador "raíz", excepto para establecer preferencias globales.

Tercero. Obtenga individualmente llamados registradores para cosas que pueden habilitarse o deshabilitarse.

log = logging.getLogger(__name__) 

func1_log = logging.getLogger("{0}.{1}".format(__name__, "my_func1") 

Ahora puede establecer niveles de registro en cada registrador con nombre.

log.setLevel(logging.INFO) 
func1_log.setLevel(logging.ERROR) 
+0

Gracias por la recomendación de poner la configuración de registro dentro del interruptor de importación principal. Bueno saber. ¿No se recomienda utilizar el registrador de raíz incluso para un script de módulo único como el que estoy trabajando ahora? ¿Podría aclarar las desventajas de hacerlo? –

+2

"utilizando el registrador de raíz no recomendado". Período. No lo uses Es "anónimo". Desea solo registradores con nombre para que pueda configurar y filtrar. –

+0

Para explicar el mensaje claro de @ S.Lott: si agrega manejadores al registrador de raíz, comenzará a recibir mensajes de ** todos los otros ** paquetes que use, lo que puede ser muy complicado (por ejemplo, 'requests'). Y viceversa, cuando otro paquete usa su paquete, los mensajes de su paquete tampoco se pueden manejar por separado. Mal diseño. – j08lue

2

Se puede usar un decorador:

import logging 
import functools 

def disable_logging(func): 
    @functools.wraps(func) 
    def wrapper(*args,**kwargs): 
     logging.disable(logging.DEBUG) 
     result = func(*args,**kwargs) 
     logging.disable(logging.NOTSET) 
     return result 
    return wrapper 

@disable_logging 
def my_func1(...): 
+0

¡Hah! Usted me gana por segundos. Tenga cuidado dentro de 'wrapper' ya que no volverá a habilitar el registro de la llamada' func() 'arroja una excepción. Es por eso que utilizo la frase 'try/finally' para garantizar que el registro se vuelva a habilitar independientemente de si' func() 'tiene éxito. –

6

Se puede crear un decorador que suspender temporalmente la tala, ala:

from functools import wraps 

def suspendlogging(func): 
    @wraps(func) 
    def inner(*args, **kwargs): 
     previousloglevel = log.getEffectiveLevel() 
     try: 
      return func(*args, **kwargs) 
     finally: 
      log.setLevel(previousloglevel) 
    return inner 

@suspendlogging 
def my_func1(): ... 

Advertencia: eso también suspendería el registro para cualquier función llamada desde my_func 1 así que ten cuidado cómo lo usas.

+0

Buen enfoque para muchas aplicaciones. ¡Gracias! –

+0

Supongo que esto debería haber sido la fábrica de decorador teniendo el registrador para suspender. –

+0

Corrígeme si me equivoco, pero me parece que esto solo desactivará un registrador llamado 'log'. Si se utilizan varios registradores al mismo tiempo, por ejemplo, en una aplicación Django, ¿cómo puede esto desactivar todos los registradores? –

0

Me tomó un tiempo aprender cómo implementar los sub-registradores como lo sugiere S.Lott.

Dado lo difícil que fue descubrir cómo configurar el registro cuando estaba comenzando, creo que ya es tiempo de compartir lo que aprendí desde entonces.

Tenga en cuenta que esta no es la única forma de configurar registradores/sub-registradores, ni es la mejor. Es la forma en que uso para hacer el trabajo para satisfacer mis necesidades. Espero que esto sea útil para alguien. Por favor, siéntete libre de comentar/compartir/criticar.


Supongamos que tenemos una biblioteca simple que nos gusta usar. Desde el programa principal, nos gustaría poder controlar los mensajes de registro que obtenemos de la biblioteca. Por supuesto, somos creadores de bibliotecas considerados, así que configuramos nuestra biblioteca para que sea más fácil.

En primer lugar, el programa principal:

# some_prog.py 

import os 
import sys 

# Be sure to give Vinay Sajip thanks for his creation of the logging module 
# and tireless efforts to answer our dumb questions about it. Thanks Vinay! 
import logging 

# This module will make understanding how Python logging works so much easier. 
# Also great for debugging why your logging setup isn't working. 
# Be sure to give it's creator Brandon Rhodes some love. Thanks Brandon! 
import logging_tree 

# Example library 
import some_lib 

# Directory, name of current module 
current_path, modulename = os.path.split(os.path.abspath(__file__)) 
modulename = modulename.split('.')[0] # Drop the '.py' 


# Set up a module-local logger 
# In this case, the logger will be named 'some_prog' 
log = logging.getLogger(modulename) 

# Add a Handler. The Handler tells the logger *where* to send the logging 
# messages. We'll set up a simple handler that send the log messages 
# to standard output (stdout) 
stdout_handler = logging.StreamHandler(stream=sys.stdout) 
log.addHandler(stdout_handler) 


def some_local_func(): 
    log.info("Info: some_local_func()") 
    log.debug("Debug: some_local_func()") 


if __name__ == "__main__": 

    # Our main program, here's where we tie together/enable the logging infra 
    # we've added everywhere else. 

    # Use logging_tree.printout() to see what the default log levels 
    # are on our loggers. Make logging_tree.printout() calls at any place in 
    # the code to see how the loggers are configured at any time. 
    # 
    # logging_tree.printout() 

    print("# Logging level set to default (i.e. 'WARNING').") 
    some_local_func() 
    some_lib.some_lib_func() 
    some_lib.some_special_func() 

    # We know a reference to our local logger, so we can set/change its logging 
    # level directly. Let's set it to INFO: 
    log.setLevel(logging.INFO) 
    print("# Local logging set to 'INFO'.") 
    some_local_func() 
    some_lib.some_lib_func() 
    some_lib.some_special_func() 


    # Next, set the local logging level to DEBUG: 
    log.setLevel(logging.DEBUG) 
    print("# Local logging set to 'DEBUG'.") 
    some_local_func() 
    some_lib.some_lib_func() 
    some_lib.some_special_func() 


    # Set the library's logging level to DEBUG. We don't necessarily 
    # have a reference to the library's logger, but we can use 
    # logging_tree.printout() to see the name and then call logging.getLogger() 
    # to create a local reference. Alternately, we could dig through the 
    # library code. 
    lib_logger_ref = logging.getLogger("some_lib") 
    lib_logger_ref.setLevel(logging.DEBUG) 

    # The library logger's default handler, NullHandler() won't output anything. 
    # We'll need to add a handler so we can see the output -- in this case we'll 
    # also send it to stdout. 
    lib_log_handler = logging.StreamHandler(stream=sys.stdout) 
    lib_logger_ref.addHandler(lib_log_handler) 
    lib_logger_ref.setLevel(logging.DEBUG) 

    print("# Logging level set to DEBUG in both local program and library.") 
    some_local_func() 
    some_lib.some_lib_func() 
    some_lib.some_special_func() 


    print("# ACK! Setting the library's logging level to DEBUG output") 
    print("# all debug messages from the library. (Use logging_tree.printout()") 
    print("# To see why.)") 
    print("# Let's change the library's logging level to INFO and") 
    print("# only some_special_func()'s level to DEBUG so we only see") 
    print("# debug message from some_special_func()") 

    # Raise the logging level of the libary and lower the logging level 
    # of 'some_special_func()' so we see only some_special_func()'s 
    # debugging-level messages. 
    # Since it is a sub-logger of the library's main logger, we don't need 
    # to create another handler, it will use the handler that belongs 
    # to the library's main logger. 
    lib_logger_ref.setLevel(logging.INFO) 
    special_func_sub_logger_ref = logging.getLogger('some_lib.some_special_func') 
    special_func_sub_logger_ref.setLevel(logging.DEBUG) 

    print("# Logging level set to DEBUG in local program, INFO in library and") 
    print("# DEBUG in some_lib.some_special_func()") 
    some_local_func() 
    some_lib.some_lib_func() 
    some_lib.some_special_func() 

A continuación, nuestra biblioteca:

# some_lib.py 

import os 
import logging 

# Directory, name of current module 
current_path, modulename = os.path.split(os.path.abspath(__file__)) 
modulename = modulename.split('.')[0] # Drop the '.py' 

# Set up a module-local logger. In this case the logger will be 
# named 'some_lib' 
log = logging.getLogger(modulename) 

# In libraries, always default to NullHandler so you don't get 
# "No handler for X" messages. 
# Let your library callers set up handlers and set logging levels 
# in their main program so the main program can decide what level 
# of messages they want to see from your library. 
log.addHandler(logging.NullHandler()) 

def some_lib_func(): 
    log.info("Info: some_lib.some_lib_func()") 
    log.debug("Debug: some_lib.some_lib_func()") 

def some_special_func(): 
    """ 
    This func is special (not really). It just has a function/method-local 
    logger in addition to the library/module-level logger. 
    This allows us to create/control logging messages down to the 
    function/method level. 

    """ 
    # Our function/method-local logger 
    func_log = logging.getLogger('%s.some_special_func' % modulename) 

    # Using the module-level logger 
    log.info("Info: some_special_func()") 

    # Using the function/method-level logger, which can be controlled separately 
    # from both the library-level logger and the main program's logger. 
    func_log.debug("Debug: some_special_func(): This message can be controlled at the function/method level.") 

Ahora vamos a ejecutar el programa, junto con la pista de comentarios:

# Logging level set to default (i.e. 'WARNING'). 

Aviso hay no hay salida en el nivel predeterminado ya que no hemos generado ningún mensaje de nivel de ADVERTENCIA.

# Local logging set to 'INFO'. 
Info: some_local_func() 

manipuladores de la biblioteca por defecto a NullHandler(), por lo que sólo ven salida del programa principal. Esto es bueno.

# Local logging set to 'DEBUG'. 
Info: some_local_func() 
Debug: some_local_func() 

Programa principal de registro establecido en DEPURAR. Todavía no vemos salida de la biblioteca. Esto es bueno.

# Logging level set to DEBUG in both local program and library. 
Info: some_local_func() 
Debug: some_local_func() 
Info: some_lib.some_lib_func() 
Debug: some_lib.some_lib_func() 
Info: some_special_func() 
Debug: some_special_func(): This message can be controlled at the function/method level. 

Oops.

# ACK! Setting the library's logging level to DEBUG output 
# all debug messages from the library. (Use logging_tree.printout() 
# To see why.) 
# Let's change the library's logging level to INFO and 
# only some_special_func()'s level to DEBUG so we only see 
# debug message from some_special_func() 
# Logging level set to DEBUG in local program, INFO in library and 
# DEBUG in some_lib.some_special_func() 
Info: some_local_func() 
Debug: some_local_func() 
Info: some_lib.some_lib_func() 
Info: some_special_func() 
Debug: some_special_func(): This message can be controlled at the function/method level. 

También es posible conseguir sólo los mensajes de depuración única de some_special_func(). Use logging_tree.printout() para averiguar qué niveles de registro modificar para que eso suceda.

Cuestiones relacionadas