我可以在记录时为gevent greenlet设置自定义名称吗?

时间:2014-02-07 06:46:33

标签: python logging gevent greenlets

当我使用标准logging包从greenlet中打印出日志语句时,我得到的文本如下:

2014-02-06 22:38:43,428 [INFO] (11396-Dummy-2) event_listener: About to block

我指的是11396-Dummy-2部分。我希望它能说出像'Main'或'Listener 1'这样的东西。这可能吗?从当前的文档中我没有看到任何API这样做。

2 个答案:

答案 0 :(得分:5)

我不确定日志中11396-Dummy-2部分的来源,但如果要向日志添加上下文信息(例如greenlet标识符),则有a number of documented ways个正在做。以下是使用LoggerAdapter

的示例
import logging
import gevent

class Adapter(logging.LoggerAdapter):
    def process(self, msg, kwargs):
        msg = '(%s) %s' % (gevent.getcurrent()._run.__name__, msg)
        return msg, kwargs

logger = Adapter(logging.getLogger(), {})

def foo():
    logger.debug('Running in foo')
    gevent.sleep(0)
    logger.debug('Explicit context switch to foo again')

def bar():
    logger.debug('Explicit context to bar')
    gevent.sleep(0)
    logger.debug('Implicit context switch back to bar')

logging.basicConfig(level=logging.DEBUG,
                    format='%(levelname)s %(threadName)s %(message)s')
gevent.joinall([
    gevent.spawn(foo),
    gevent.spawn(bar),
])

运行时,应打印

DEBUG MainThread (foo) Running in foo
DEBUG MainThread (bar) Explicit context to bar
DEBUG MainThread (foo) Explicit context switch to foo again
DEBUG MainThread (bar) Implicit context switch back to bar

答案 1 :(得分:3)

注意,如果threading模块被猴子修补,则线程几乎映射到greenlets。特别是猴子补丁替换_start_new_thread()(因此它开始一个新的greenlet),以及_get_ident()(因此每当要求线程ID时都会返回greenlet ID)。感谢这个映射,每当你询问当前线程时,实际上你得到一个与当前运行greenlet关联的虚拟Thread对象实例!

因此,完全有可能做到以下几点:

import gevent.monkey
gevent.monkey.patch_thread()

from threading import current_thread

# and then, at the start of the greenlet
current_thread().name = "MyNewName" 

现在,只要logging代码检索当前线程名称,它就会获得每个greenlet名称。我承认这有点像黑客,但它在我目前的项目中运作良好。

这是一个概念证明:

import gevent.monkey
gevent.monkey.patch_thread()

import logging
from threading import current_thread

logger = logging.getLogger()

def foo():
    current_thread().name = "MyFoo" 
    logger.debug('Running in foo')
    gevent.sleep(0)
    logger.debug('Explicit context switch to foo again')

def bar():
    current_thread().name = "MyBar" 
    logger.debug('Explicit context to bar')
    gevent.sleep(0)
    logger.debug('Implicit context switch back to bar')

logging.basicConfig(level=logging.DEBUG,
                    format='%(levelname)s %(threadName)s %(message)s')
gevent.joinall([
    gevent.spawn(foo),
    gevent.spawn(bar),
])

执行时,会打印:

DEBUG MyFoo Running in foo
DEBUG MyBar Explicit context to bar
DEBUG MyFoo Explicit context switch to foo again
DEBUG MyBar Implicit context switch back to bar

确保在进行任何其他导入之前修补threading模块(请参阅this answer)。