validacion usuarios usuario sesiones registro personalizado manejo logueo grupos python logging

usuarios - Cómo agregar un nivel de registro personalizado a las funciones de registro de Python



usuario personalizado django (14)

¿Quién comenzó la mala práctica de usar métodos internos ( self._log ) y por qué cada respuesta se basa en eso? La solución pythonic sería usar self.log en self.log lugar para no tener que meterse con ninguna cosa interna:

import logging SUBDEBUG = 5 logging.addLevelName(SUBDEBUG, ''SUBDEBUG'') def subdebug(self, message, *args, **kws): self.log(SUBDEBUG, message, *args, **kws) logging.Logger.subdebug = subdebug logging.basicConfig() l = logging.getLogger() l.setLevel(SUBDEBUG) l.subdebug(''test'') l.setLevel(logging.DEBUG) l.subdebug(''test'')

Me gustaría tener loglevel TRACE (5) para mi aplicación, ya que no creo que debug() sea ​​suficiente. Además log(5, msg) no es lo que quiero. ¿Cómo puedo agregar un nivel de registro personalizado a un registrador de Python?

Tengo un mylogger.py con el siguiente contenido:

import logging @property def log(obj): myLogger = logging.getLogger(obj.__class__.__name__) return myLogger

En mi código lo uso de la siguiente manera:

class ExampleClass(object): from mylogger import log def __init__(self): ''''''The constructor with the logger'''''' self.log.debug("Init runs")

Ahora me gustaría llamar a self.log.trace("foo bar")

Gracias de antemano por tu ayuda.

Editar (8 de diciembre de 2016): Cambié la respuesta aceptada a pfa''s que es, en mi humilde opinión, una excelente solución basada en la muy buena propuesta de Eric S.


@Eric S.

La respuesta de Eric S. es excelente, pero aprendí por experimentación que esto siempre provocará que los mensajes registrados en el nuevo nivel de depuración se impriman, independientemente de en qué nivel de registro esté establecido. Por lo tanto, si crea un nuevo número de nivel de 9, si llama a setLevel (50), los mensajes de nivel inferior se imprimirán erróneamente. Para evitar que esto suceda, necesita otra línea dentro de la función "debugv" para verificar si el nivel de registro en cuestión está realmente habilitado.

Ejemplo fijo que verifica si el nivel de registro está habilitado:

import logging DEBUG_LEVELV_NUM = 9 logging.addLevelName(DEBUG_LEVELV_NUM, "DEBUGV") def debugv(self, message, *args, **kws): # Yes, logger takes its ''*args'' as ''args''. if self.isEnabledFor(DEBUG_LEVELV_NUM): self._log(DEBUG_LEVELV_NUM, message, args, **kws) logging.Logger.debugv = debugv

Si observa el código de la class Logger en logging.__init__.py para Python 2.7, esto es lo que hacen todas las funciones de log estándar (.critical, .debug, etc.).

Aparentemente, no puedo publicar respuestas a las respuestas de los demás por falta de reputación ... espero que Eric actualice su publicación si ve esto. =)


Además del ejemplo de Mad Physicists para obtener el nombre de archivo y el número de línea correctos:

def logToRoot(message, *args, **kwargs): if logging.root.isEnabledFor(levelNum): logging.root._log(levelNum, message, args, **kwargs)


Combinando todas las respuestas existentes con un montón de experiencia en el uso, creo que he creado una lista de todas las cosas que se deben hacer para garantizar un uso completamente integrado del nuevo nivel. Los pasos siguientes suponen que está agregando un nuevo nivel de logging.DEBUG - 5 == 5 con logging.DEBUG - 5 == 5 valor. logging.DEBUG - 5 == 5 :

  1. logging.addLevelName(logging.DEBUG - 5, ''TRACE'') necesita ser invocado para obtener el nuevo nivel registrado internamente para que pueda ser referenciado por su nombre.
  2. El nuevo nivel debe agregarse como un atributo para el logging por sí mismo para la coherencia: logging.TRACE = logging.DEBUG - 5 .
  3. Se debe agregar un método llamado trace al módulo de logging . Debería comportarse como debug , info , etc.
  4. Se debe agregar un método llamado trace a la clase de registrador actualmente configurada. Como esto no está 100% garantizado como logging.Logger . logging.Logger , use logging.getLoggerClass() lugar.

Todos los pasos se ilustran en el siguiente método:

def addLoggingLevel(levelName, levelNum, methodName=None): """ Comprehensively adds a new logging level to the `logging` module and the currently configured logging class. `levelName` becomes an attribute of the `logging` module with the value `levelNum`. `methodName` becomes a convenience method for both `logging` itself and the class returned by `logging.getLoggerClass()` (usually just `logging.Logger`). If `methodName` is not specified, `levelName.lower()` is used. To avoid accidental clobberings of existing attributes, this method will raise an `AttributeError` if the level name is already an attribute of the `logging` module or if the method name is already present Example ------- >>> addLoggingLevel(''TRACE'', logging.DEBUG - 5) >>> logging.getLogger(__name__).setLevel("TRACE") >>> logging.getLogger(__name__).trace(''that worked'') >>> logging.trace(''so did this'') >>> logging.TRACE 5 """ if not methodName: methodName = levelName.lower() if hasattr(logging, levelName): raise AttributeError(''{} already defined in logging module''.format(levelName)) if hasattr(logging, methodName): raise AttributeError(''{} already defined in logging module''.format(methodName)) if hasattr(logging.getLoggerClass(), methodName): raise AttributeError(''{} already defined in logger class''.format(methodName)) # This method was inspired by the answers to post # http://.com/q/2183233/2988730, especially # http://.com/a/13638084/2988730 def logForLevel(self, message, *args, **kwargs): if self.isEnabledFor(levelNum): self._log(levelNum, message, args, **kwargs) def logToRoot(message, *args, **kwargs): logging.log(levelNum, message, *args, **kwargs) logging.addLevelName(levelNum, levelName) setattr(logging, levelName, levelNum) setattr(logging.getLoggerClass(), methodName, logForLevel) setattr(logging, methodName, logToRoot)


Como alternativa a agregar un método extra a la clase Logger, recomendaría usar el Logger.log(level, msg) .

import logging TRACE = 5 logging.addLevelName(TRACE, ''TRACE'') FORMAT = ''%(levelname)s:%(name)s:%(lineno)d:%(message)s'' logging.basicConfig(format=FORMAT) l = logging.getLogger() l.setLevel(TRACE) l.log(TRACE, ''trace message'') l.setLevel(logging.DEBUG) l.log(TRACE, ''disabled trace message'')


Creo que tendrás que subclasificar la clase Logger y agregar un método llamado trace que básicamente llama a Logger.log con un nivel más bajo que DEBUG . No lo he intentado pero esto es lo que indican los documentos .


En caso de que alguien quiera una forma automática de agregar un nuevo nivel de registro al módulo de registro (o una copia) dinámicamente, he creado esta función, expandiendo la respuesta de @ pfa:

def add_level(log_name,custom_log_module=None,log_num=None, log_call=None, lower_than=None, higher_than=None, same_as=None, verbose=True): '''''' Function to dynamically add a new log level to a given custom logging module. <custom_log_module>: the logging module. If not provided, then a copy of <logging> module is used <log_name>: the logging level name <log_num>: the logging level num. If not provided, then function checks <lower_than>,<higher_than> and <same_as>, at the order mentioned. One of those three parameters must hold a string of an already existent logging level name. In case a level is overwritten and <verbose> is True, then a message in WARNING level of the custom logging module is established. '''''' if custom_log_module is None: import imp custom_log_module = imp.load_module(''custom_log_module'', *imp.find_module(''logging'')) log_name = log_name.upper() def cust_log(par, message, *args, **kws): # Yes, logger takes its ''*args'' as ''args''. if par.isEnabledFor(log_num): par._log(log_num, message, args, **kws) available_level_nums = [key for key in custom_log_module._levelNames if isinstance(key,int)] available_levels = {key:custom_log_module._levelNames[key] for key in custom_log_module._levelNames if isinstance(key,str)} if log_num is None: try: if lower_than is not None: log_num = available_levels[lower_than]-1 elif higher_than is not None: log_num = available_levels[higher_than]+1 elif same_as is not None: log_num = available_levels[higher_than] else: raise Exception(''Infomation about the ''+ ''log_num should be provided'') except KeyError: raise Exception(''Non existent logging level name'') if log_num in available_level_nums and verbose: custom_log_module.warn(''Changing '' + custom_log_module._levelNames[log_num] + '' to ''+log_name) custom_log_module.addLevelName(log_num, log_name) if log_call is None: log_call = log_name.lower() exec(''custom_log_module.Logger.''+eval(''log_call'')+'' = cust_log'', None, locals()) return custom_log_module


En mi experiencia, esta es la solución completa, el problema de la operación ... para evitar ver "lambda" como la función en la que se emite el mensaje, profundiza:

MY_LEVEL_NUM = 25 logging.addLevelName(MY_LEVEL_NUM, "MY_LEVEL_NAME") def log_at_my_log_level(self, message, *args, **kws): # Yes, logger takes its ''*args'' as ''args''. self._log(MY_LEVEL_NUM, message, args, **kws) logger.log_at_my_log_level = log_at_my_log_level

Nunca he intentado trabajar con una clase de registrador independiente, pero creo que la idea básica es la misma (use _log).


Esta pregunta es bastante antigua, pero acabo de tratar el mismo tema y encontré una manera similar a las ya mencionadas que me parece un poco más clara. Esto se probó en 3.4, por lo que no estoy seguro de si los métodos utilizados existen en versiones anteriores:

from logging import getLoggerClass, addLevelName, setLoggerClass, NOTSET VERBOSE = 5 class MyLogger(getLoggerClass()): def __init__(self, name, level=NOTSET): super().__init__(name, level) addLevelName(VERBOSE, "VERBOSE") def verbose(self, msg, *args, **kwargs): if self.isEnabledFor(VERBOSE): self._log(VERBOSE, msg, args, **kwargs) setLoggerClass(MyLogger)


Esto funcionó para mí:

import logging logging.basicConfig( format='' %(levelname)-8.8s %(funcName)s: %(message)s'', ) logging.NOTE = 32 # positive yet important logging.addLevelName(logging.NOTE, ''NOTE'') # new level logging.addLevelName(logging.CRITICAL, ''FATAL'') # rename existing log = logging.getLogger(__name__) log.note = lambda msg, *args: log._log(logging.NOTE, msg, args) log.note(''school/'s out for summer! %s'', ''dude'') log.fatal(''file not found.'')

El problema lambda / funcName se soluciona con logger._log como @marqueed señaló. Creo que usar lambda parece un poco más limpio, pero el inconveniente es que no puede tomar los argumentos de palabra clave. Nunca he usado eso yo mismo, así que no hay problema.

NOTE setup: school''s out for summer! dude FATAL setup: file not found.


Estoy confundido; con Python 3.5, al menos, simplemente funciona:

import logging TRACE = 5 """more detail than debug""" logging.basicConfig() logging.addLevelName(TRACE,"TRACE") logger = logging.getLogger('''') logger.debug("n") logger.setLevel(logging.DEBUG) logger.debug("y1") logger.log(TRACE,"n") logger.setLevel(TRACE) logger.log(TRACE,"y2")

salida:

DEBUG: root: y1

TRACE: raíz: y2


Me resulta más fácil crear un nuevo atributo para el objeto logger que pasa la función log (). Creo que el módulo logger proporciona el addLevelName () y el log () por esta misma razón. Por lo tanto, no se necesitan subclases ni nuevos métodos.

import logging @property def log(obj): logging.addLevelName(5, ''TRACE'') myLogger = logging.getLogger(obj.__class__.__name__) setattr(myLogger, ''trace'', lambda *args: myLogger.log(5, *args)) return myLogger

ahora

mylogger.trace(''This is a trace message'')

debería funcionar como se espera


Sugerencias para crear un registrador personalizado:

  1. No use _log , use log (no tiene que marcar isEnabledFor )
  2. el módulo de registro debe ser la instancia de creación del registrador personalizado, ya que hace algo de magia en getLogger , por lo que tendrá que establecer la clase a través de setLoggerClass
  3. No necesita definir __init__ para el registrador, clase si no está almacenando nada

# Lower than debug which is 10 TRACE = 5 class MyLogger(logging.Logger): def trace(self, msg, *args, **kwargs): self.log(TRACE, msg, *args, **kwargs)

Cuando llame a este registrador use setLoggerClass(MyLogger) para hacer que este sea el registrador predeterminado de getLogger

logging.setLoggerClass(MyLogger) log = logging.getLogger(__name__) # ... log.trace("something specific")

Necesitará setFormatter , setHandler y setLevel(TRACE) en el handler y en el log para ver realmente este rastro de bajo nivel


Tomé la respuesta "avoid seeing lambda" y tuve que modificar dónde se agregaba log_at_my_log_level. Yo también vi el problema que hizo Paul "No creo que esto funcione. ¿No necesitas el registrador como primer argumento en log_at_my_log_level?" Esto funcionó para mí

import logging DEBUG_LEVELV_NUM = 9 logging.addLevelName(DEBUG_LEVELV_NUM, "DEBUGV") def debugv(self, message, *args, **kws): # Yes, logger takes its ''*args'' as ''args''. self._log(DEBUG_LEVELV_NUM, message, args, **kws) logging.Logger.debugv = debugv