Mi cadena de formato actual es:
formatter = logging.Formatter('%(asctime)s : %(message)s')
y quiero agregar un nuevo campo llamado app_name
que tendrá un valor diferente en cada script que contiene este formateador.
import logging
formatter = logging.Formatter('%(asctime)s %(app_name)s : %(message)s')
syslog.setFormatter(formatter)
logger.addHandler(syslog)
Pero no estoy seguro de cómo pasar ese app_name
valor al registrador para interpolarlo en la cadena de formato. Obviamente, puedo hacer que aparezca en el mensaje de registro pasándolo cada vez, pero esto es complicado.
He intentado:
logging.info('Log message', app_name='myapp')
logging.info('Log message', {'app_name', 'myapp'})
logging.info('Log message', 'myapp')
pero ninguno funciona.
log
llamada? Si es así, mire los documentos donde dice "Esta funcionalidad puede usarse para inyectar sus propios valores en un LogRecord ..." Pero este parece ser un caso excelente para usarlologger = logging.getLogger('myapp')
y tenerlo integrado en lalogger.info
llamada.logger
objeto en cada aplicación, puede hacer que cada uno de los usos un nombre diferente creando instancias de suslogger
s de este modo:logger = logging.getLogger(myAppName)
. tenga en cuenta que__name__
es el nombre del módulo de Python, por lo que si cada aplicación es su propio módulo de Python, eso también funcionaría.Respuestas:
Puede usar un LoggerAdapter para no tener que pasar la información adicional con cada llamada 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')
registros (algo así como)
2013-07-09 17:39:33,596 Super App : The sky is so blue
Los filtros también se pueden utilizar para agregar información contextual.
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')
produce un registro de registro similar.
fuente
config.ini
archivo? Deseo agregar el nombre de host actualsocket.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)
Necesita pasar el dict como parámetro a extra para hacerlo de esa manera.
logging.info('Log message', extra={'app_name': 'myapp'})
Prueba:
>>> import logging >>> logging.basicConfig(format="%(foo)s - %(message)s") >>> logging.warning('test', extra={'foo': 'bar'}) bar - test
Además, como nota, si intenta registrar un mensaje sin pasar el dict, fallará.
>>> 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
fuente
logging.info()
? Falló cuando lo intenté por última vez. : /logging.Formatter
clase: class CustomFormatter (logging.Formatter): def format (self, record): if not hasattr (record, 'foo'): record.foo = 'default_foo' return super (CustomFormatter, self.format (registro) 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 de Python3.2 ahora puede 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
Por supuesto,
record_factory
se puede personalizar para que sea invocable y el valor decustom_attribute
podría actualizarse si mantiene una referencia al invocable de fábrica.¿Por qué es mejor que usar adaptadores / filtros?
logger = logging.getLogger(..)
) y ahora tendrán el mismo formato de registro. (este no es el caso con Filtros / Adaptadores donde necesita usar el mismo objeto de registrador)fuente
Otra forma es crear un LoggerAdapter personalizado. Esto es particularmente útil cuando no puede cambiar el formato O si su formato se comparte con un código que no envía la clave única (en su 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
Y en su código, crearía e inicializaría su registrador como de costumbre:
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, crearía el adaptador de contenedor para agregar un prefijo según sea necesario:
logger = LoggerAdapter(logger, 'myapp') logger.info('The world bores you when you are cool.')
La salida se verá así:
2013-07-09 17:39:33,596 [myapp] The world bores you when you are cool.
fuente
Encontré esta pregunta SO después de implementarla yo mismo. Espero que ayude a alguien. En el siguiente código, estoy induciendo una clave adicional llamada
claim_id
en formato de registrador. Registrará el Claim_id siempre que haya unaclaim_id
clave presente en el entorno. En mi caso de uso, necesitaba registrar esta información para una función de 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")
Gist: https://gist.github.com/ramanujam/306f2e4e1506f302504fb67abef50652
fuente
Usando la respuesta de mr2ert, se me ocurrió esta solución cómoda (aunque supongo que no es recomendable): anule los métodos de registro integrados para aceptar el argumento personalizado y crear el
extra
diccionario dentro de los 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')
Salida:
2019-03-02 20:06:51,998 [bar] test
Esta es la función incorporada para referencia:
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)
fuente
registro de importación;
clase 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:% (mensaje ) s ');
clase 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)
clase Prueba: logger = Logger.getLogger ('Prueba')
if __name__=='__main__': logger.logMessage('ERROR','123','This is an error')
fuente