如何将自定义字段添加到Python日志格式字符串?

nic*_*ine 68 python logging

我当前的格式字符串是:

formatter = logging.Formatter('%(asctime)s : %(message)s')
Run Code Online (Sandbox Code Playgroud)

我想添加一个名为app_name的新字段,它将在包含此格式化程序的每个脚本中具有不同的值.

import logging
formatter = logging.Formatter('%(asctime)s %(app_name)s : %(message)s')
syslog.setFormatter(formatter)
logger.addHandler(syslog)
Run Code Online (Sandbox Code Playgroud)

但我不知道如何将该app_name值传递给记录器以插入格式字符串.我显然可以让它出现在日志消息中,但每次都传递它,但这很麻烦.

我试过了:

logging.info('Log message', app_name='myapp')
logging.info('Log message', {'app_name', 'myapp'})
logging.info('Log message', 'myapp')
Run Code Online (Sandbox Code Playgroud)

但没有工作.

unu*_*tbu 104

您可以使用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')
Run Code Online (Sandbox Code Playgroud)

日志(类似的东西)

2013-07-09 17:39:33,596 Super App : The sky is so blue
Run Code Online (Sandbox Code Playgroud)

过滤器还可用于添加上下文信息.

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')
Run Code Online (Sandbox Code Playgroud)

产生类似的日志记录.

  • 我们如何在`config.ini`文件中指定它?我希望添加当前主机名`socket.gethostname()`. (3认同)
  • 我可以只传递一串额外信息吗?这样的事情:“雇员ID 1029382发生错误”,而没有创建任何词典。 (2认同)

mr2*_*ert 42

你需要将dict作为参数传递给extra,以便那样做.

logging.info('Log message', extra={'app_name': 'myapp'})
Run Code Online (Sandbox Code Playgroud)

证明:

>>> import logging
>>> logging.basicConfig(format="%(foo)s - %(message)s")
>>> logging.warning('test', extra={'foo': 'bar'})
bar - test 
Run Code Online (Sandbox Code Playgroud)

另外,作为注释,如果您尝试在不传递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
Run Code Online (Sandbox Code Playgroud)

  • 救了我的一天:) (3认同)
  • 我喜欢@ mr2ert答案。您可以通过扩展`logging.Formatter`类为附加字段提供默认值:class CustomFormatter(logging.Formatter):def format(self,record):如果没有hasattr(record,'foo'):record.foo ='default_foo'返回超级(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!') (2认同)

Ahm*_*mad 36

蟒蛇3

从 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)
Run Code Online (Sandbox Code Playgroud)
>>> logging.info("hello")
my-attr - hello
Run Code Online (Sandbox Code Playgroud)

当然,record_factory可以自定义为任何可调用对象,custom_attribute如果您保留对工厂可调用对象的引用,则可以更新的值。

为什么这比使用适配器/过滤器更好?

  • 您不需要在应用程序周围传递记录器
  • 它实际上适用于使用自己的记录器(只需调用logger = logging.getLogger(..))的3rd 方库现在具有相同的日志格式。(过滤器/适配器不是这种情况,您需要使用相同的记录器对象)
  • 您可以堆叠/链接多个工厂

  • 这是当今 python3 的最佳答案 (7认同)

rou*_*ble 8

另一种方法是创建自定义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
Run Code Online (Sandbox Code Playgroud)

在您的代码中,您将像往常一样创建并初始化您的记录器:

    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)
Run Code Online (Sandbox Code Playgroud)

最后,您将创建包装器适配器以根据需要添加前缀:

    logger = LoggerAdapter(logger, 'myapp')
    logger.info('The world bores you when you are cool.')
Run Code Online (Sandbox Code Playgroud)

输出看起来像这样:

2013-07-09 17:39:33,596 [myapp] The world bores you when you are cool.
Run Code Online (Sandbox Code Playgroud)


rhn*_*n89 7

我自己实现后发现了这个SO问题。希望它能帮助某人。在下面的代码中,我引入了一个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")
Run Code Online (Sandbox Code Playgroud)

要点:https://gist.github.com/ygivenx/306f2e4e1506f302504fb67abef50652


小智 5

接受的答案没有在日志文件中记录格式,而格式反映在系统输出中。或者,我使用了一种更简单的方法并担任:

logging.basicConfig(filename="mylogfile.test",
                    filemode="w+",
                    format='%(asctime)s: ' +app_name+': %(message)s ',
                    level=logging.DEBUG)
Run Code Online (Sandbox Code Playgroud)