-
-
Save simon-weber/7755289 to your computer and use it in GitHub Desktop.
import tornado | |
class RequestContextHandler(tornado.web.RequestHandler): | |
def _execute(self, transforms, *args, **kwargs): | |
# following the example of: | |
# https://github.com/bdarnell/tornado_tracing/blob/master/tornado_tracing/recording.py | |
global_data = {} # add whatever here, e.g. self.request | |
with tornado.stack_context.StackContext(functools.partial(ThreadRequestContext, **global_data)): | |
super(RequestContextHandler, self)._execute(transforms, *args, **kwargs) | |
# elsewhere, use ThreadRequestContext.data => a dict |
import threading | |
class ThreadRequestContext(object): | |
"""A context manager that saves some per-thread state globally. | |
Intended for use with Tornado's StackContext. | |
Provide arbitrary data as kwargs upon creation, | |
then use ThreadRequestContext.data to access it. | |
""" | |
_state = threading.local() | |
_state.data = {} | |
class __metaclass__(type): | |
# property() doesn't work on classmethods, | |
# see http://stackoverflow.com/q/128573/1231454 | |
@property | |
def data(cls): | |
if not hasattr(cls._state, 'data'): | |
return {} | |
return cls._state.data | |
def __init__(self, **data): | |
self._data = data | |
def __enter__(self): | |
self._prev_data = self.__class__.data | |
self.__class__._state.data = self._data | |
def __exit__(self, *exc): | |
self.__class__._state.data = self._prev_data | |
del self._prev_data | |
return False |
For some reason when I try and use this gist (tornado 4.1) I get this exception:
Traceback (most recent call last):
File "/home/uber/zipkintegration/clay-tornado/env/local/lib/python2.7/site-packages/tornado/http1connection.py", line 236, in _read_message
delegate.finish()
File "/home/uber/zipkintegration/clay-tornado/env/local/lib/python2.7/site-packages/tornado/httpserver.py", line 269, in finish
self.delegate.finish()
File "/home/uber/zipkintegration/clay-tornado/env/local/lib/python2.7/site-packages/tornado/web.py", line 1898, in finish
self.execute()
File "/home/uber/zipkintegration/clay-tornado/env/local/lib/python2.7/site-packages/tornado/web.py", line 1930, in execute
f.add_done_callback(lambda f: f.exception())
AttributeError: 'NoneType' object has no attribute 'add_done_callback'
When I update line 13 to include a return
I don't get the exception anymore:
with tornado.stack_context.StackContext(functools.partial(ThreadRequestContext, **global_data)):
return super(RequestContextHandler, self)._execute(transforms, *args, **kwargs)
You think this is the correct fix? Perhaps we can update the gist to reflect this.
For which version of Python and Tornado is this for? I'm trying to use this and that just completely fails. The RequestThreadContext.data throws a NameError: data is not a property of the class. The metaclass seems just to not work. I tried to replace it with a class method and change data for data() in my code. It works, but the thread local information is not transmitted across method boundaries. Use of internal undocumented Tornado method (_execute, saw that nowhere) and funky metaclass stuff is likely to bind this to very specific environments.
I'm using Python 3.4.3 and Tornado 3.2.
Hey, sorry for missing these comments -- I didn't realize github doesn't notify on gist comments and mentions don't work. Shoot me a tweet to let me know if you post something new.
likely to bind this to very specific environments.
Ah, yeah. This is working for me in python 2.7.x + tornado 3.2.2, but I haven't tested it elsewhere.
Can you add a practical example of how to use this?
I hook this into a root logging filter, so all logging from the application can have the user id prepended when it's available.
Not work well with coroutine
Those who come by here may want to try https://gist.github.com/virtuald/50bf7cacdc8cfb05e323f350539f0efa instead.
Can you add a practical example of how to use this? Literally just access
ThreadRequestContext.data
from anywhere and it will respect the context switches?