91 votes

Enregistrement des données variables avec une nouvelle chaîne de format

J'utilise la fonction de journalisation pour python 2.7.3. La documentation pour cette version de Python dit :

le paquet de journalisation est antérieur aux nouvelles options de formatage telles que str.format() et string.Template. Ces nouvelles options de formatage sont prises en charge...

J'aime le format "nouveau" avec des accolades. J'essaie donc de faire quelque chose comme.. :

 log = logging.getLogger("some.logger")
 log.debug("format this message {0}", 1)

Et j'obtiens une erreur :

TypeError : tous les arguments n'ont pas été convertis lors du formatage des chaînes de caractères.

Qu'est-ce que je rate ici ?

P.S. Je ne veux pas utiliser

log.debug("format this message {0}".format(1))

car dans ce cas, le message est toujours formaté, quel que soit le niveau de journalisation.

0voto

Dragorn421 Points 144

Solution similaire à pR0Ps' , enveloppant getMessage sur LogRecord par l'emballage makeRecord (au lieu de handle dans leur réponse) dans les cas de Logger qui devrait être activé par le nouveau formatage :

def getLogger(name):
    log = logging.getLogger(name)
    def Logger_makeRecordWrapper(name, level, fn, lno, msg, args, exc_info, func=None, extra=None, sinfo=None):
        self = log
        record = logging.Logger.makeRecord(self, name, level, fn, lno, msg, args, exc_info, func, sinfo)
        def LogRecord_getMessageNewStyleFormatting():
            self = record
            msg = str(self.msg)
            if self.args:
                msg = msg.format(*self.args)
            return msg
        record.getMessage = LogRecord_getMessageNewStyleFormatting
        return record
    log.makeRecord = Logger_makeRecordWrapper
    return log

J'ai testé cela avec Python 3.5.3.

0voto

Karolius Points 8

Combiné string.Formatter à ajouter pprint.pformat et de la conversion de type logging : setLogRecordFactory , setLoggerClass . Il y a une astuce intéressante : je crée un tuple imbriqué supplémentaire pour l'argument args para Logger._log puis de le décompresser dans LogRecord init pour omettre les surcharges dans Logger.makeRecord . Utilisation de log.f wraps chaque attribut (méthodes de journalisation à dessein) avec use_format donc vous n'avez pas besoin de l'écrire explicitement. Cette solution est rétrocompatible.

from collections import namedtuple
from collections.abc import Mapping                                                     
from functools import partial                                                          
from pprint import pformat                                                              
from string import Formatter                                                        
import logging                

Logger = logging.getLoggerClass()                                                       
LogRecord = logging.getLogRecordFactory()                                               

class CustomFormatter(Formatter):                                                       
    def format_field(self, value, format_spec):                                         
        if format_spec.endswith('p'):                                                   
            value = pformat(value)                                                      
            format_spec = format_spec[:-1]                                              
        return super().format_field(value, format_spec)                                 

custom_formatter = CustomFormatter()                                                    

class LogWithFormat:                                                                    
    def __init__(self, obj):                                                            
        self.obj = obj                                                                  

    def __getattr__(self, name):                                                        
        return partial(getattr(self.obj, name), use_format=True)    

ArgsSmuggler = namedtuple('ArgsSmuggler', ('args', 'smuggled'))                                                                                     

class CustomLogger(Logger):                                                             
    def __init__(self, *ar, **kw):                                                      
        super().__init__(*ar, **kw)                                                     
        self.f = LogWithFormat(self)                                                    

    def _log(self, level, msg, args, *ar, use_format=False, **kw):                                         
        super()._log(level, msg, ArgsSmuggler(args, use_format), *ar, **kw)                                     

class CustomLogRecord(LogRecord):                                                       
    def __init__(self, *ar, **kw):                                                   
        args = ar[5]
        # RootLogger use CustomLogRecord but not CustomLogger
        # then just unpack only ArgsSmuggler instance
        args, use_format = args if isinstance(args, ArgsSmuggler) else (args, False)                                       
        super().__init__(*ar[:5], args, *ar[6:], **kw)                                  
        self.use_format = use_format                                                    

    def getMessage(self):                                                               
        return self.getMessageWithFormat() if self.use_format else super().getMessage() 

    def getMessageWithFormat(self):                                                     
        msg = str(self.msg)                                                             
        args = self.args                                                                
        if args:                                                                        
            fmt = custom_formatter.format                                               
            msg = fmt(msg, **args) if isinstance(args, Mapping) else fmt(msg, *args)    
        return msg                                                                      

logging.setLogRecordFactory(CustomLogRecord)      
logging.setLoggerClass(CustomLogger)              

log = logging.getLogger(__name__)   
log.info('%s %s', dict(a=1, b=2), 5)          
log.f.info('{:p} {:d}', dict(a=1, b=2), 5)

-1voto

Dutch Masters Points 470

Voici quelque chose de très simple qui fonctionne :

debug_logger: logging.Logger = logging.getLogger("app.debug")

def mydebuglog(msg: str, *args, **kwargs):
    if debug_logger.isEnabledFor(logging.DEBUG):
        debug_logger.debug(msg.format(*args, **kwargs))

Ensuite :

mydebuglog("hello {} {val}", "Python", val="World")

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X