#36094: A PicklingError occurs when using Django channels together with a 
logging
SocketHandler, if an HTTP error status is returned.
-----------------------------+----------------------------------------
     Reporter:  Vinay Sajip  |                     Type:  Bug
       Status:  new          |                Component:  Core (Other)
      Version:  5.1          |                 Severity:  Normal
     Keywords:               |             Triage Stage:  Unreviewed
    Has patch:  0            |      Needs documentation:  0
  Needs tests:  0            |  Patch needs improvement:  0
Easy pickings:  0            |                    UI/UX:  0
-----------------------------+----------------------------------------
 Assume that the logging configuration includes a
 `logging.handlers.SocketHandler`. When a view returns an HTTP error status
 such as 400, the behavior is different according to whether or not
 channels are in use. When channels are ''not'' in use, you get this
 printed on the console:

 {{{
 Bad Request: /fail/400
 [13/Jan/2025 21:39:04] "GET /fail/400 HTTP/1.1" 400 26314
 }}}

 Well and good. However, if channels ''are'' in use, you get this:

 {{{
 Bad Request: /fail/400
 --- Logging error ---
 Traceback (most recent call last):
   File "/usr/lib/python3.10/logging/handlers.py", line 677, in emit
     s = self.makePickle(record)
   File "/usr/lib/python3.10/logging/handlers.py", line 649, in makePickle
     s = pickle.dumps(d, 1)
   File "/home/vinay/.local/share/virtualenvs/django-base/lib/python3.10
 /site-packages/django/urls/resolvers.py", line 105, in __reduce_ex__
     raise PicklingError(f"Cannot pickle {self.__class__.__qualname__}.")
 _pickle.PicklingError: Cannot pickle ResolverMatch.
 Call stack:
   File "/usr/lib/python3.10/threading.py", line 973, in _bootstrap
     self._bootstrap_inner()
   File "/usr/lib/python3.10/threading.py", line 1016, in _bootstrap_inner
     self.run()
   File "/usr/lib/python3.10/threading.py", line 953, in run
     self._target(*self._args, **self._kwargs)
   File "/usr/lib/python3.10/concurrent/futures/thread.py", line 83, in
 _worker
     work_item.run()
   File "/usr/lib/python3.10/concurrent/futures/thread.py", line 58, in run
     result = self.fn(*self.args, **self.kwargs)
   File "/home/vinay/.local/share/virtualenvs/django-base/lib/python3.10
 /site-packages/asgiref/sync.py", line 522, in thread_handler
     return func(*args, **kwargs)
   File "/home/vinay/.local/share/virtualenvs/django-base/lib/python3.10
 /site-packages/django/utils/log.py", line 248, in log_response
     getattr(logger, level)(
 Message: '%s: %s'
 Arguments: ('Bad Request', '/fail/400')
 HTTP GET /fail/400 400 [0.24, 127.0.0.1:49610]
 }}}

 The reason appears to be due to this code in `django.utils.log.py`, in
 function `log_response`:
 {{{
     getattr(logger, level)(
         message,
         *args,
         extra={
             "status_code": response.status_code,
             "request": request,
         },
         exc_info=exception,
     )
 }}}

 The difference between the channels case and the other is that in the
 channels case, the `extra` dictionary contains the `request` key points to
 an instance of `django.core.handlers.asgi.ASGIRequest`, whereas in the
 non-channels case the `request` key points to an instance of
 `django.core.handlers.wsgi.WSGIRequest`. In the WSGI request handling
 path, the `resolver_match` attribute of the request is not changed after
 being initialized to `None`, whereas in the ASGI request handling path,
 the `resolver_match` attribute is set to a `ResolverMatch` instance, which
 is not pickleable, and leads to the above traceback.

 Furthermore, when looking at this problem, the `ASGIRequest.__init__` sets
 the `resolver_match` attribute to `None` twice. Either one of those is
 redundant, or a comment is needed to say why the two set statements are
 present.
-- 
Ticket URL: <https://code.djangoproject.com/ticket/36094>
Django <https://code.djangoproject.com/>
The Web framework for perfectionists with deadlines.

-- 
You received this message because you are subscribed to the Google Groups 
"Django updates" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/django-updates/010701946086eeb2-4c37a04b-6ffc-4e17-8367-45d36856c043-000000%40eu-central-1.amazonses.com.

Reply via email to