我当前的格式字符串是:
formatter = logging.Formatter('%(asctime)s : %(message)s')
我想添加一个名为app_name的新字段,该字段在包含此格式化程序的每个脚本中都有不同的值。
import logging
formatter = logging.Formatter('%(asctime)s %(app_name)s : %(message)s')
syslog.setFormatter(formatter)
logger.addHandler(syslog)
但我不确定如何将app_name
值传递给记录器以插入格式字符串。我显然可以让它出现在日志消息中,但每次都会传递它,但这很麻烦。
我试过了:
logging.info('Log message', app_name='myapp')
logging.info('Log message', {'app_name', 'myapp'})
logging.info('Log message', 'myapp')
但没有工作。
答案 0 :(得分:90)
您可以使用LoggerAdapter,这样您就不必为每次记录调用传递额外的信息:
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')
日志(类似的东西)
2013-07-09 17:39:33,596 Super App : The sky is so blue
Filters也可用于添加上下文信息。
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')
生成类似的日志记录。
答案 1 :(得分:33)
你需要将dict作为参数传递给extra,以便那样做。
logging.info('Log message', extra={'app_name': 'myapp'})
证明:
>>> import logging
>>> logging.basicConfig(format="%(foo)s - %(message)s")
>>> logging.warning('test', extra={'foo': 'bar'})
bar - test
另外,作为注释,如果您尝试在不传递dict的情况下记录消息,那么它将失败。
>>> 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
答案 2 :(得分:7)
从Python3.2开始,您现在可以使用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
当然,record_factory
可以自定义为任何可调用对象,并且如果保留对工厂可调用对象的引用,则custom_attribute
的值可以更新。
logger = logging.getLogger(..)
)现在具有相同的日志格式。 (对于“过滤器/适配器”,情况并非如此,您需要使用同一记录器对象)答案 3 :(得分:6)
另一种方法是创建自定义LoggerAdapter。如果您无法更改格式,或者如果您的格式与不发送唯一密钥的代码共享(在您的情况下为 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
在您的代码中,您将像往常一样创建并初始化您的记录器:
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)
最后,您将创建包装器适配器以根据需要添加前缀:
logger = LoggerAdapter(logger, 'myapp')
logger.info('The world bores you when you are cool.')
输出看起来像这样:
2013-07-09 17:39:33,596 [myapp] The world bores you when you are cool.
答案 4 :(得分:1)
我自己实施此问题后就发现了它。希望它能帮助某人。在下面的代码中,我在记录器格式中引入了一个称为claim_id
的额外密钥。每当环境中存在claim_id
密钥时,它将记录claim_id。在我的用例中,我需要记录此信息以使用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")
要点:https://gist.github.com/ramanujam/306f2e4e1506f302504fb67abef50652
答案 5 :(得分:1)
如果您需要一个默认的 extra
映射,并且您想为临时日志消息自定义它,这可以在 Python 2.7+ 中通过创建一个 LoggerAdapter
将默认 extra
字典与给定消息中的任何 extra
合并。
import logging
import os
import sys
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s %(levelname)-8s Py%(python)-4s pid:%(pid)-5s %(message)s',
)
_logger = logging.getLogger("my-logger")
_logger.setLevel(logging.DEBUG)
class DefaultExtrasAdapter(logging.LoggerAdapter):
def __init__(self, logger, extra):
super(DefaultExtrasAdapter, self).__init__(logger, extra)
def process(self, msg, kwargs):
# Speed gain if no extras are present
if "extra" in kwargs:
copy = dict(self.extra).copy()
copy.update(kwargs["extra"])
kwargs["extra"] = copy
else:
kwargs["extra"] = self.extra
return msg, kwargs
LOG = DefaultExtrasAdapter(_logger, {"python": sys.version_info[0], "pid": os.getpid()})
if __name__ == "__main__":
LOG.info("<-- With defaults")
LOG.info("<-- With my version", extra={"python": 3.10})
LOG.info("<-- With my pid", extra={"pid": 0})
LOG.info("<-- With both", extra={"python": 2.7, "pid": -1})
结果:
2021-08-05 18:58:27,308 INFO Py2 pid:8435 <-- With defaults
2021-08-05 18:58:27,309 INFO Py3.1 pid:8435 <-- With my version
2021-08-05 18:58:27,309 INFO Py2 pid:0 <-- With my pid
2021-08-05 18:58:27,309 INFO Py2.7 pid:-1 <-- With both
答案 6 :(得分:0)
使用mr2ert的答案,我想出了一个舒适的解决方案(尽管我不建议这样做)-覆盖内置的日志记录方法以接受自定义参数并在方法内部创建extra
字典:>
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')
输出:
2019-03-02 20:06:51,998 [bar] test
这是供参考的内置函数
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)
答案 7 :(得分:0)
导入日志记录;
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: %(message)s');
类记录器:
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)
类测试: logger = Logger.getLogger('Test')
if __name__=='__main__':
logger.logMessage('ERROR','123','This is an error')