Minha string de formato atual é:
formatter = logging.Formatter('%(asctime)s : %(message)s')
e desejo adicionar um novo campo chamado, app_name
que terá um valor diferente em cada script que contém esse formatador.
import logging
formatter = logging.Formatter('%(asctime)s %(app_name)s : %(message)s')
syslog.setFormatter(formatter)
logger.addHandler(syslog)
Mas não tenho certeza de como passar esse app_name
valor para o logger para interpolar na string de formato. Obviamente, posso fazer com que apareça na mensagem de log passando-o todas as vezes, mas isso é confuso.
Eu tentei:
logging.info('Log message', app_name='myapp')
logging.info('Log message', {'app_name', 'myapp'})
logging.info('Log message', 'myapp')
mas nenhum funciona.
log
chamadas? Em caso afirmativo, olhe para os documentos onde diz "Esta funcionalidade pode ser usada para injetar seus próprios valores em um LogRecord ..." Mas este parece ser o caso principal para usarlogger = logging.getLogger('myapp')
e incluí- lo nalogger.info
chamada.logger
objeto em cada aplicativo, você pode fazer cada um usar um nome diferente por instanciar seuslogger
s assim:logger = logging.getLogger(myAppName)
. observe que__name__
é o nome do módulo python, portanto, se cada aplicativo for seu próprio módulo python, isso também funcionaria.Respostas:
Você poderia usar um LoggerAdapter para não ter que passar as informações extras a cada chamada de registro:
import logging extra = {'app_name':'Super App'} logger = logging.getLogger(__name__) syslog = logging.StreamHandler() formatter = logging.Formatter('%(asctime)s %(app_name)s : %(message)s') syslog.setFormatter(formatter) logger.setLevel(logging.INFO) logger.addHandler(syslog) logger = logging.LoggerAdapter(logger, extra) logger.info('The sky is so blue')
logs (algo como)
2013-07-09 17:39:33,596 Super App : The sky is so blue
Os filtros também podem ser usados para adicionar informações contextuais.
import logging class AppFilter(logging.Filter): def filter(self, record): record.app_name = 'Super App' return True logger = logging.getLogger(__name__) logger.addFilter(AppFilter()) syslog = logging.StreamHandler() formatter = logging.Formatter('%(asctime)s %(app_name)s : %(message)s') syslog.setFormatter(formatter) logger.setLevel(logging.INFO) logger.addHandler(syslog) logger.info('The sky is so blue')
produz um registro de log semelhante.
fonte
config.ini
arquivo? Desejo adicionar o nome do host atualsocket.gethostname()
.import uuid uniqueId = str(uuid.uuid4()) extra = {"u_id" : uniqueId} RotatingHandler = RotatingFileHandler(LOG_FILENAME,encoding='utf-8',maxBytes=maxSize, backupCount=batchSize) logger.basicConfig(handlers=[RotatingHandler],level=logLevel.upper(),format='%(levelname)s %(u_id)s %(funcName)s %(asctime)s %(message)s ',datefmt='%m/%d/%Y %I:%M:%S %p') logger = logger.LoggerAdapter(logger=logger, extra=extra)
Você precisa passar o dict como um parâmetro para extra para fazer isso dessa maneira.
logging.info('Log message', extra={'app_name': 'myapp'})
Prova:
>>> import logging >>> logging.basicConfig(format="%(foo)s - %(message)s") >>> logging.warning('test', extra={'foo': 'bar'}) bar - test
Além disso, como observação, se você tentar registrar uma mensagem sem passar o dict, ela falhará.
>>> logging.warning('test') Traceback (most recent call last): File "/usr/lib/python2.7/logging/__init__.py", line 846, in emit msg = self.format(record) File "/usr/lib/python2.7/logging/__init__.py", line 723, in format return fmt.format(record) File "/usr/lib/python2.7/logging/__init__.py", line 467, in format s = self._fmt % record.__dict__ KeyError: 'foo' Logged from file <stdin>, line 1
fonte
logging.info()
? Ele falhou na última vez que tentei. : /logging.Formatter
classe: class CustomFormatter (logging.Formatter): def formato (self, record): if not hasattr (record, 'foo'): record.foo = 'default_foo' return super (CustomFormatter, self.format (record) h = loggin.StreamHandler () h.setFormatter (CustomFormatter ('% (foo) s% (message) s') logger = logging.getLogger ('bar') logger.addHandler ( h) logger.error ('hey!', extra = {'foo': 'FOO'}) logger.error ('hey!')Python3
A partir do Python 3.2, você agora pode usar LogRecordFactory
>>> import logging >>> logging.basicConfig(format="%(custom_attribute)s - %(message)s") >>> old_factory = logging.getLogRecordFactory() >>> def record_factory(*args, **kwargs): record = old_factory(*args, **kwargs) record.custom_attribute = "my-attr" return record >>> logging.setLogRecordFactory(record_factory) >>> logging.info("hello") my-attr - hello
É claro que
record_factory
pode ser personalizado para qualquer chamada e o valor decustom_attribute
pode ser atualizado se você mantiver uma referência para a chamada de fábrica.Por que isso é melhor do que usar adaptadores / filtros?
logger = logging.getLogger(..)
) e agora têm o mesmo formato de log. (este não é o caso com Filtros / Adaptadores onde você precisa usar o mesmo objeto logger)fonte
Outra maneira é criar um LoggerAdapter personalizado. Isso é particularmente útil quando você não pode alterar o formato OU se o seu formato é compartilhado com um código que não envia a chave exclusiva (no seu caso app_name ):
class LoggerAdapter(logging.LoggerAdapter): def __init__(self, logger, prefix): super(LoggerAdapter, self).__init__(logger, {}) self.prefix = prefix def process(self, msg, kwargs): return '[%s] %s' % (self.prefix, msg), kwargs
E em seu código, você criaria e inicializaria seu registrador como de costume:
logger = logging.getLogger(__name__) # Add any custom handlers, formatters for this logger myHandler = logging.StreamHandler() myFormatter = logging.Formatter('%(asctime)s %(message)s') myHandler.setFormatter(myFormatter) logger.addHandler(myHandler) logger.setLevel(logging.INFO)
Finalmente, você criaria o adaptador de wrapper para adicionar um prefixo conforme necessário:
logger = LoggerAdapter(logger, 'myapp') logger.info('The world bores you when you are cool.')
O resultado será mais ou menos assim:
2013-07-09 17:39:33,596 [myapp] The world bores you when you are cool.
fonte
Eu encontrei esta pergunta SO depois de implementá-la sozinho. Espero que ajude alguém. No código abaixo, estou induzindo uma chave extra chamada
claim_id
no formato logger. Ele registrará o Claim_id sempre que houver umaclaim_id
chave presente no ambiente. No meu caso de uso, precisei registrar essas informações para uma função do AWS Lambda.import logging import os LOG_FORMAT = '%(asctime)s %(name)s %(levelname)s %(funcName)s %(lineno)s ClaimID: %(claim_id)s: %(message)s' class AppLogger(logging.Logger): # Override all levels similarly - only info overriden here def info(self, msg, *args, **kwargs): return super(AppLogger, self).info(msg, extra={"claim_id": os.getenv("claim_id", "")}) def get_logger(name): """ This function sets log level and log format and then returns the instance of logger""" logging.setLoggerClass(AppLogger) logging.basicConfig(level=logging.INFO, format=LOG_FORMAT) logger = logging.getLogger(name) logger.setLevel(logging.INFO) return logger LOGGER = get_logger(__name__) LOGGER.info("Hey") os.environ["claim_id"] = "12334" LOGGER.info("Hey")
Síntese: https://gist.github.com/ramanujam/306f2e4e1506f302504fb67abef50652
fonte
A resposta aceita não registrou o formato no arquivo de log, enquanto o formato foi refletido na saída sys. Como alternativa, usei uma abordagem mais simples e trabalhei como;
fonte
Usando a resposta de mr2ert, eu vim com esta solução confortável (embora eu ache que não seja recomendada) - Substitua os métodos de registro integrados para aceitar o argumento personalizado e criar o
extra
dicionário dentro dos métodos:import logging class CustomLogger(logging.Logger): def debug(self, msg, foo, *args, **kwargs): extra = {'foo': foo} if self.isEnabledFor(logging.DEBUG): self._log(logging.DEBUG, msg, args, extra=extra, **kwargs) *repeat for info, warning, etc* logger = CustomLogger('CustomLogger', logging.DEBUG) formatter = logging.Formatter('%(asctime)s [%(foo)s] %(message)s') handler = logging.StreamHandler() handler.setFormatter(formatter) logger.addHandler(handler) logger.debug('test', 'bar')
Resultado:
2019-03-02 20:06:51,998 [bar] test
Esta é a função integrada para referência:
def debug(self, msg, *args, **kwargs): """ Log 'msg % args' with severity 'DEBUG'. To pass exception information, use the keyword argument exc_info with a true value, e.g. logger.debug("Houston, we have a %s", "thorny problem", exc_info=1) """ if self.isEnabledFor(DEBUG): self._log(DEBUG, msg, args, **kwargs)
fonte
registro de importação;
classe LogFilter (logging.Filter):
def __init__(self, code): self.code = code def filter(self, record): record.app_code = self.code return True
logging.basicConfig (format = '[% (asctime) s:% (levelname) s] :: [% (module) s ->% (name) s] - APP_CODE:% (app_code) s - MSG:% (mensagem ) s ');
classe Logger:
def __init__(self, className): self.logger = logging.getLogger(className) self.logger.setLevel(logging.ERROR) @staticmethod def getLogger(className): return Logger(className) def logMessage(self, level, code, msg): self.logger.addFilter(LogFilter(code)) if level == 'WARN': self.logger.warning(msg) elif level == 'ERROR': self.logger.error(msg) else: self.logger.info(msg)
classe Teste: logger = Logger.getLogger ('Teste')
if __name__=='__main__': logger.logMessage('ERROR','123','This is an error')
fonte