For a Pyramid project, I have been looking for a way to stick a unique request ID on every generated log lines. I wanted something like:
2012-08-02 15:19:55,402 INFO [worker 4][request=SOME_UNIQUE_ID] Message here!
This way I can grep through my logs for a particular request ID and extract all logs lines generated by this one request. This is especially useful when multiple requests have interlaced log lines in the output.
![]() |
| Dog tag of a request |
Generate a unique ID
First off, we have to generate a unique ID for each request and have it accessible directly on the object:>>> request.id 'cc30495a-73ba-40be-978f-49cc64822049'
For this, we need to extend the default
Request class used by Pyramid with our own class.
from uuid import uuid4
from pyramid.request import Request
from pyramid.decorator import reify
class MyRequest(Request):
@reify
def id(self):
return str(uuid4())
Then we have to configure our Pyramid project -- in the main __init__.py file -- to use this request class of ours.
# import our MyRequest class
# ...
def main(global_config, **settings):
config = Configurator(settings=settings)
config.set_request_factory(MyRequest)
Great. Now all our requests will have the request.id attribute.
Inject the unique ID in the logs
Now we have to find a way to print the request ID in our logs. Some options:- Create a custom logging method that retrieves the request object by using the get_current_request() functionality of Pyramid and inject the request ID in the log message. This solution is not very elegant as we have to maintain a homemade logger and change all our code to use it. Booooring.
- Create a Pyramid Tween to hack in the request ID into thread's name, e.g., "worker 4" that is already shown in our logs. Now THAT sounds like fun! :)
The tween
So here is our tween.import threading
def hack_thread_name_tween_factory(handler, registry):
def hack_thread_name_tween(request):
# Hack in the request ID inside the thread's name
current_thread = threading.current_thread()
original_name = current_thread.name
current_thread.name = "%s][request=%s" % (original_name, request.id)
try:
response = handler(request)
finally:
# Restore the thread's original name when done
current_thread.name = original_name
return response
return hack_thread_name_tween
Now let's configure our Pyramid project to use that tween.
# import our MyRequest class
# ...
def main(global_config, **settings):
config = Configurator(settings=settings)
config.set_request_factory(MyRequest)
config.add_tween('myproject.lib.tweens.hack_thread_name_tween_factory')
And here is the resulting log when hitting your server!
2012-08-02 15:19:55,402 INFO [worker 4][request=cc30495a-73ba-40be-978f-49cc64822049] Message here!
Now whenever we see something wrong in our logs, we can grep the request ID of the culprit and find out how it behaved.
$ grep cc30495a-73ba-40be-978f-49cc64822049 app-out.log 2012-08-02 15:19:55,402 INFO [worker 4][request=cc30495a-73ba-40be-978f-49cc64822049] All is good here! 2012-08-02 15:19:55,524 INFO [worker 4][request=cc30495a-73ba-40be-978f-49cc64822049] Everything's a-ok! 2012-08-02 15:19:55,598 ERROR [worker 4][request=cc30495a-73ba-40be-978f-49cc64822049] Aaah, I'm failing here!
The faster way
Of course, you could skip the subclassing of theRequest class and generate the UUID directly in the tween. But I thought it might come in handy to have the request.id available for future reference, besides from the fact that I already had a custom Request class for other purpose.

Wonderful, thanks for taking the time to write this up! This is just what I needed in my app, as a way to differentiate between different requests.
ReplyDeleteI did change to generate a random 5 digit string instead of a UUID so that it would take less horizontal space when reading logs:
chars = 'abcdefghijkmnopqrstuvwxyz23456789'
return ''.join(random.choice(chars) for _ in range(5))
Thanks for the note, I'm glad you found it useful and relevant after almost a decade later of writing this post :) Have fun!
Delete