write values print logger log example disable_existing_loggers python logging string-formatting

values - ¿Se puede modificar el formato de registro de Python dependiendo del nivel de registro de mensajes?



python logging level values (7)

Estoy usando el mecanismo de logging de Python para imprimir el resultado en la pantalla. Podría hacer esto con declaraciones de impresión, pero quiero permitir una granularidad más precisa para que el usuario desactive ciertos tipos de resultados. Me gusta el formato impreso para errores, pero preferiría un formato más simple cuando el nivel de salida es "información".

Por ejemplo:

logger.error("Running cmd failed") logger.info("Running cmd passed")

En este ejemplo, me gustaría que el formato del error se imprima de manera diferente:

# error Aug 27, 2009 - ERROR: Running cmd failed # info Running cmd passed

¿Es posible tener diferentes formatos para diferentes niveles de registro sin tener múltiples objetos de registro? Preferiría hacer esto sin modificar el registrador una vez que se haya creado, ya que hay una gran cantidad de sentencias if / else para determinar cómo se debe registrar la salida.


Acabo de toparme con este problema y tuve problemas para completar los "agujeros" que quedan en el ejemplo anterior. Aquí hay una versión más completa y funcional que utilicé. Espero que esto ayude a alguien:

# Custom formatter class MyFormatter(logging.Formatter): err_fmt = "ERROR: %(msg)s" dbg_fmt = "DBG: %(module)s: %(lineno)d: %(msg)s" info_fmt = "%(msg)s" def __init__(self, fmt="%(levelno)s: %(msg)s"): logging.Formatter.__init__(self, fmt) def format(self, record): # Save the original format configured by the user # when the logger formatter was instantiated format_orig = self._fmt # Replace the original format with one customized by logging level if record.levelno == logging.DEBUG: self._fmt = MyFormatter.dbg_fmt elif record.levelno == logging.INFO: self._fmt = MyFormatter.info_fmt elif record.levelno == logging.ERROR: self._fmt = MyFormatter.err_fmt # Call the original formatter class to do the grunt work result = logging.Formatter.format(self, record) # Restore the original format configured by the user self._fmt = format_orig return result

Editar:

Felicitaciones de Halloleo, aquí hay un ejemplo de cómo usar lo anterior en tu script:

fmt = MyFormatter() hdlr = logging.StreamHandler(sys.stdout) hdlr.setFormatter(fmt) logging.root.addHandler(hdlr) logging.root.setLevel(DEBUG)

Editar 2:

El registro de Python3 ha cambiado un poco. Vea here para un acercamiento de Python3.


En lugar de depender de estilos o campos internos, también puede crear un formateador que delegue en otros formateadores dependiendo de record.levelno (u otros criterios). Esta es una solución ligeramente más limpia en mi humilde opinión. El siguiente código debería funcionar para cualquier versión de Python> = 2.7:

La manera simple se vería así:

class MyFormatter(logging.Formatter): default_fmt = logging.Formatter(''%(levelname)s in %(name)s: %(message)s'') info_fmt = logging.Formatter(''%(message)s'') def format(self, record): if record.levelno == logging.INFO: return self.info_fmt.format(record) else: return self.default_fmt.format(record)

Pero podrías hacerlo más genérico:

class VarFormatter(logging.Formatter): default_formatter = logging.Formatter(''%(levelname)s in %(name)s: %(message)s'') def __init__(self, formats): """ formats is a dict { loglevel : logformat } """ self.formatters = {} for loglevel in formats: self.formatters[loglevel] = logging.Formatter(formats[loglevel]) def format(self, record): formatter = self.formatters.get(record.levelno, self.default_formatter) return formatter.format(record)

Utilicé un dict como entrada aquí, pero obviamente también puedes usar tuplas, ** kwargs, lo que sea que flote tu bote. Esto luego se usaría como:

formatter = VarFormatter({logging.INFO: ''[%(message)s]'', logging.WARNING: ''warning: %(message)s''}) <... attach formatter to logger ...>


Esta es una adaptación de la respuesta de estani a la nueva implementación de logging.Formatter que ahora se basa en estilos de formateo. El mío se basa en ''{'' formato de estilo ''{'' , pero se puede adaptar. Podría ser refinado para ser más general y permitir la selección de estilo de formato y mensajes personalizados como argumentos para __init__ , también.

class SpecialFormatter(logging.Formatter): FORMATS = {logging.DEBUG : logging._STYLES[''{'']("{module} DEBUG: {lineno}: {message}"), logging.ERROR : logging._STYLES[''{'']("{module} ERROR: {message}"), logging.INFO : logging._STYLES[''{'']("{module}: {message}"), ''DEFAULT'' : logging._STYLES[''{'']("{module}: {message}")} def format(self, record): # Ugly. Should be better self._style = self.FORMATS.get(record.levelno, self.FORMATS[''DEFAULT'']) return logging.Formatter.format(self, record) hdlr = logging.StreamHandler(sys.stderr) hdlr.setFormatter(SpecialFormatter()) logging.root.addHandler(hdlr) logging.root.setLevel(logging.INFO)


La solución anterior funciona con la versión 3.3.3. Sin embargo, con 3.3.4 obtienes el siguiente error.

FORMATS = { logging.DEBUG : logging._STYLES[''{'']("{module} DEBUG: {lineno}: {message}"),

TypeError: el objeto ''tuple'' no se puede llamar

Después de buscar en la clase de registro Lib / logging__init __. Py encontré que una estructura de datos ha cambiado de 3.3.3 a 3.3.4 que causa el problema

3.3.3

_STYLES = { ''%'': PercentStyle, ''{'': StrFormatStyle, ''$'': StringTemplateStyle }

3.3.4

_STYLES = { ''%'': (PercentStyle, BASIC_FORMAT), ''{'': (StrFormatStyle, ''{levelname}:{name}:{message} AA''), ''$'': (StringTemplateStyle, ''${levelname}:${name}:${message} BB''), }

La solución actualizada es por lo tanto

class SpecialFormatter(logging.Formatter): FORMATS = {logging.DEBUG : logging._STYLES[''{''][0]("{module} DEBUG: {lineno}: {message}"), logging.ERROR : logging._STYLES[''{''][0]("{module} ERROR: {message}"), logging.INFO : logging._STYLES[''{''][0]("{module}: {message}"), ''DEFAULT'' : logging._STYLES[''{''][0]("{module}: {message}")} def format(self, record): # Ugly. Should be better self._style = self.FORMATS.get(record.levelno, self.FORMATS[''DEFAULT'']) return logging.Formatter.format(self, record)


Sí, puedes hacer esto teniendo una clase de Formatter personalizada:

class MyFormatter(logging.Formatter): def format(self, record): #compute s according to record.levelno #for example, by setting self._fmt #according to the levelno, then calling #the superclass to do the actual formatting return s

A continuación, adjunte una instancia de MyFormatter a sus controladores.


Si solo desea omitir el formateo de ciertos niveles, puede hacer algo más simple que las otras respuestas como las siguientes:

class FormatterNotFormattingInfo(logging.Formatter): def __init__(self, fmt = ''%(levelname)s:%(message)s''): logging.Formatter.__init__(self, fmt) def format(self, record): if record.levelno == logging.INFO: return record.getMessage() return logging.Formatter.format(self, record)

Esto también tiene la ventaja de trabajar antes y después de la versión 3.2 al no usar variables internas como self._fmt ni self._style.


Y de nuevo, como respuesta JS, pero más compacto.

class SpecialFormatter(logging.Formatter): FORMATS = {logging.DEBUG :"DBG: %(module)s: %(lineno)d: %(message)s", logging.ERROR : "ERROR: %(message)s", logging.INFO : "%(message)s", ''DEFAULT'' : "%(levelname)s: %(message)s"} def format(self, record): self._fmt = self.FORMATS.get(record.levelno, self.FORMATS[''DEFAULT'']) return logging.Formatter.format(self, record) hdlr = logging.StreamHandler(sys.stderr) hdlr.setFormatter(SpecialFormatter()) logging.root.addHandler(hdlr) logging.root.setLevel(logging.INFO)