为每个线程定义记录器

时间:2012-06-12 09:54:25

标签: python multithreading logging

我有一个用于处理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 deco应该是怎样的。我想出了一个解决方法,为每个请求使用单独的进程,然后在每个进程中设置根记录器将完全符合我的要求。有没有办法让这项工作不被迫使用多处理?

谢谢!

1 个答案:

答案 0 :(得分:1)

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

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的应用程序的不同部分使用不同的格式化程序。