Note, that if threading
module is monkey-patched, threads are pretty much mapped to greenlets. In particular monkey patching replaces _start_new_thread()
(so it starts a new greenlet instead), as well as _get_ident()
(so greenlet ID is returned whenever a thread id is being asked for). Thanks to this mapping, whenever you ask about current thread, in fact you get a dummy Thread
object instance associated with currently running greenlet!
Thus, it is perfectly possible to do the following:
import gevent.monkey
gevent.monkey.patch_thread()
from threading import current_thread
# and then, at the start of the greenlet
current_thread().name = "MyNewName"
Now, whenever logging
code retrieves current thread name, it gets per-greenlet name. I've to admit it's a bit of a hack, but it works pretty well in my current project.
And here's a proof of concept:
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),
])
When executed, it prints:
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
Just ensure that threading
module is patched before any other import (see this answer).