2

我有一个用于处理 XMLRPC 请求的服务器应用程序。每个连接都创建自己的线程。我想设置日志记录,以便一些线程/连接特定信息进入日志。我可以做类似的事情:

import logging

@add_unique_request_id
def thread_top(request_id):
    logging.info('thread %d says: hello'%request_id)
    logging.error('thread %d says: darn!!'%request_id)

并按照我的意愿设置全局根记录器,但我不喜欢这个解决方案。我想要以下内容:

import logging

@setup_logger
def thread_top():
    logging.info('hello')
    logging.error('darn!!')

但我不知道 setup_logger 装饰应该是什么样子。我想出了一个解决方法,为每个请求使用单独的进程,然后在每个进程中设置根记录器将完全符合我的要求。有什么方法可以在不被迫使用多处理的情况下完成这项工作吗?

谢谢!

4

1 回答 1

1

我正在使用自定义格式化程序和线程本地存储来执行此操作:

from collections import defaultdict
import logging
import threading

class ContextAwareFormatter(logging.Formatter):
    """
    Makes use of get_context() to populate the record attributes.
    """

    def format(self, record):
        # Using defaultdict to avoid KeyErrorS when a key is not in the context.
        def factory():
            return ""
        record.__dict__ = defaultdict(factory, record.__dict__)

        for k, v in get_context().iteritems():
            if not hasattr(record, k):
                setattr(record, k, v)
        return logging.Formatter.format(self, record)

THREADLOCAL_ATTR = "logging_context"
_threadlocal = threading.local()

def get_context():
    result = getattr(_threadlocal, THREADLOCAL_ATTR, None)
    if result is None:
        result = {}
        setattr(_threadlocal, THREADLOCAL_ATTR, result)
    return result

def set_context(**context):
    c = get_context()
    c.clear()
    c.update(**context)
    return c

def update_context(**context):
    c = get_context()
    c.update(**context)
    return c

然后在记录器配置中:

"formatters": {
    "default": {
        "()": "log.ContextAwareFormatter",
        "format": "%(asctime)s %(levelname)s [%(request_id)s] %(message)s (%(module)s:%(lineno)d)",
    },
}

在记录上下文之前填充:

update_context(request_id=request_id)

request_id您可能希望在日志记录中可能不需要的应用程序的不同部分使用不同的格式化程序。

于 2012-06-12T10:23:49.760 回答