django-structlog 9.0.0.dev1__tar.gz → 9.0.0.dev2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (23) hide show
  1. {django_structlog-9.0.0.dev1/django_structlog.egg-info → django_structlog-9.0.0.dev2}/PKG-INFO +1 -2
  2. {django_structlog-9.0.0.dev1 → django_structlog-9.0.0.dev2}/django_structlog/__init__.py +1 -1
  3. {django_structlog-9.0.0.dev1 → django_structlog-9.0.0.dev2}/django_structlog/app_settings.py +4 -4
  4. {django_structlog-9.0.0.dev1 → django_structlog-9.0.0.dev2}/django_structlog/apps.py +1 -1
  5. {django_structlog-9.0.0.dev1 → django_structlog-9.0.0.dev2}/django_structlog/celery/receivers.py +32 -75
  6. {django_structlog-9.0.0.dev1 → django_structlog-9.0.0.dev2}/django_structlog/celery/steps.py +1 -3
  7. {django_structlog-9.0.0.dev1 → django_structlog-9.0.0.dev2}/django_structlog/commands.py +5 -14
  8. {django_structlog-9.0.0.dev1 → django_structlog-9.0.0.dev2}/django_structlog/middlewares/request.py +23 -42
  9. {django_structlog-9.0.0.dev1 → django_structlog-9.0.0.dev2/django_structlog.egg-info}/PKG-INFO +1 -2
  10. {django_structlog-9.0.0.dev1 → django_structlog-9.0.0.dev2}/django_structlog.egg-info/SOURCES.txt +0 -1
  11. {django_structlog-9.0.0.dev1 → django_structlog-9.0.0.dev2}/pyproject.toml +0 -20
  12. django_structlog-9.0.0.dev1/django_structlog/py.typed +0 -0
  13. {django_structlog-9.0.0.dev1 → django_structlog-9.0.0.dev2}/LICENSE.rst +0 -0
  14. {django_structlog-9.0.0.dev1 → django_structlog-9.0.0.dev2}/MANIFEST.in +0 -0
  15. {django_structlog-9.0.0.dev1 → django_structlog-9.0.0.dev2}/README.rst +0 -0
  16. {django_structlog-9.0.0.dev1 → django_structlog-9.0.0.dev2}/django_structlog/celery/__init__.py +0 -0
  17. {django_structlog-9.0.0.dev1 → django_structlog-9.0.0.dev2}/django_structlog/celery/signals.py +0 -0
  18. {django_structlog-9.0.0.dev1 → django_structlog-9.0.0.dev2}/django_structlog/middlewares/__init__.py +0 -0
  19. {django_structlog-9.0.0.dev1 → django_structlog-9.0.0.dev2}/django_structlog/signals.py +0 -0
  20. {django_structlog-9.0.0.dev1 → django_structlog-9.0.0.dev2}/django_structlog.egg-info/dependency_links.txt +0 -0
  21. {django_structlog-9.0.0.dev1 → django_structlog-9.0.0.dev2}/django_structlog.egg-info/requires.txt +0 -0
  22. {django_structlog-9.0.0.dev1 → django_structlog-9.0.0.dev2}/django_structlog.egg-info/top_level.txt +0 -0
  23. {django_structlog-9.0.0.dev1 → django_structlog-9.0.0.dev2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: django-structlog
3
- Version: 9.0.0.dev1
3
+ Version: 9.0.0.dev2
4
4
  Summary: Structured Logging for Django
5
5
  Author-email: Jules Robichaud-Gagnon <j.robichaudg+pypi@gmail.com>
6
6
  License: MIT
@@ -24,7 +24,6 @@ Classifier: Programming Language :: Python :: 3.13
24
24
  Classifier: Topic :: System :: Logging
25
25
  Classifier: License :: OSI Approved :: MIT License
26
26
  Classifier: Operating System :: OS Independent
27
- Classifier: Typing :: Typed
28
27
  Requires-Python: >=3.9
29
28
  Description-Content-Type: text/x-rst
30
29
  License-File: LICENSE.rst
@@ -3,6 +3,6 @@
3
3
 
4
4
  name = "django_structlog"
5
5
 
6
- VERSION = (9, 0, 0, "dev1")
6
+ VERSION = (9, 0, 0, "dev2")
7
7
 
8
8
  __version__ = ".".join(str(v) for v in VERSION)
@@ -8,19 +8,19 @@ class AppSettings:
8
8
  PREFIX = "DJANGO_STRUCTLOG_"
9
9
 
10
10
  @property
11
- def CELERY_ENABLED(self) -> bool:
11
+ def CELERY_ENABLED(self):
12
12
  return getattr(settings, self.PREFIX + "CELERY_ENABLED", False)
13
13
 
14
14
  @property
15
- def STATUS_4XX_LOG_LEVEL(self) -> int:
15
+ def STATUS_4XX_LOG_LEVEL(self):
16
16
  return getattr(settings, self.PREFIX + "STATUS_4XX_LOG_LEVEL", logging.WARNING)
17
17
 
18
18
  @property
19
- def COMMAND_LOGGING_ENABLED(self) -> bool:
19
+ def COMMAND_LOGGING_ENABLED(self):
20
20
  return getattr(settings, self.PREFIX + "COMMAND_LOGGING_ENABLED", False)
21
21
 
22
22
  @property
23
- def USER_ID_FIELD(self) -> str:
23
+ def USER_ID_FIELD(self):
24
24
  return getattr(settings, self.PREFIX + "USER_ID_FIELD", "pk")
25
25
 
26
26
 
@@ -6,7 +6,7 @@ from .app_settings import app_settings
6
6
  class DjangoStructLogConfig(AppConfig):
7
7
  name = "django_structlog"
8
8
 
9
- def ready(self) -> None:
9
+ def ready(self):
10
10
  if app_settings.CELERY_ENABLED:
11
11
  from .celery.receivers import CeleryReceiver
12
12
 
@@ -1,7 +1,3 @@
1
- from __future__ import annotations
2
-
3
- from typing import Type, Any, Optional, cast, TYPE_CHECKING
4
-
5
1
  import structlog
6
2
  from celery import current_app
7
3
  from celery.signals import (
@@ -18,27 +14,23 @@ from celery.signals import (
18
14
 
19
15
  from . import signals
20
16
 
21
- if TYPE_CHECKING:
22
- from types import TracebackType
23
17
 
24
18
  logger = structlog.getLogger(__name__)
25
19
 
26
20
 
27
21
  class CeleryReceiver:
28
- _priority: Optional[str]
29
-
30
- def __init__(self) -> None:
22
+ def __init__(self):
31
23
  self._priority = None
32
24
 
33
25
  def receiver_before_task_publish(
34
26
  self,
35
- sender: Optional[Type[Any]] = None,
36
- headers: Optional[dict[str, Any]] = None,
37
- body: Optional[dict[str, str]] = None,
38
- properties: Optional[dict[str, Optional[str]]] = None,
39
- routing_key: Optional[str] = None,
40
- **kwargs: dict[str, str],
41
- ) -> None:
27
+ sender=None,
28
+ headers=None,
29
+ body=None,
30
+ properties=None,
31
+ routing_key=None,
32
+ **kwargs,
33
+ ):
42
34
  if current_app.conf.task_protocol < 2:
43
35
  return
44
36
 
@@ -54,17 +46,12 @@ class CeleryReceiver:
54
46
  )
55
47
  if properties:
56
48
  self._priority = properties.get("priority", None)
57
- if headers is not None:
58
- headers["__django_structlog__"] = context
49
+
50
+ headers["__django_structlog__"] = context
59
51
 
60
52
  def receiver_after_task_publish(
61
- self,
62
- sender: Optional[Type[Any]] = None,
63
- headers: Optional[dict[str, Optional[str]]] = None,
64
- body: Optional[dict[str, Optional[str]]] = None,
65
- routing_key: Optional[str] = None,
66
- **kwargs: Any,
67
- ) -> None:
53
+ self, sender=None, headers=None, body=None, routing_key=None, **kwargs
54
+ ):
68
55
  properties = {}
69
56
  if self._priority is not None:
70
57
  properties["priority"] = self._priority
@@ -72,23 +59,13 @@ class CeleryReceiver:
72
59
 
73
60
  logger.info(
74
61
  "task_enqueued",
75
- child_task_id=(
76
- headers.get("id")
77
- if headers
78
- else cast(dict[str, Optional[str]], body).get("id")
79
- ),
80
- child_task_name=(
81
- headers.get("task")
82
- if headers
83
- else cast(dict[str, Optional[str]], body).get("task")
84
- ),
62
+ child_task_id=headers.get("id") if headers else body.get("id"),
63
+ child_task_name=headers.get("task") if headers else body.get("task"),
85
64
  routing_key=routing_key,
86
65
  **properties,
87
66
  )
88
67
 
89
- def receiver_task_prerun(
90
- self, task_id: str, task: Any, *args: Any, **kwargs: Any
91
- ) -> None:
68
+ def receiver_task_prerun(self, task_id, task, *args, **kwargs):
92
69
  structlog.contextvars.clear_contextvars()
93
70
  structlog.contextvars.bind_contextvars(task_id=task_id)
94
71
  metadata = getattr(task.request, "__django_structlog__", {})
@@ -98,18 +75,10 @@ class CeleryReceiver:
98
75
  )
99
76
  logger.info("task_started", task=task.name)
100
77
 
101
- def receiver_task_retry(
102
- self,
103
- request: Optional[Any] = None,
104
- reason: Optional[str] = None,
105
- einfo: Optional[Any] = None,
106
- **kwargs: Any,
107
- ) -> None:
78
+ def receiver_task_retry(self, request=None, reason=None, einfo=None, **kwargs):
108
79
  logger.warning("task_retrying", reason=reason)
109
80
 
110
- def receiver_task_success(
111
- self, result: Optional[str] = None, **kwargs: Any
112
- ) -> None:
81
+ def receiver_task_success(self, result=None, **kwargs):
113
82
  signals.pre_task_succeeded.send(
114
83
  sender=self.receiver_task_success, logger=logger, result=result
115
84
  )
@@ -117,14 +86,14 @@ class CeleryReceiver:
117
86
 
118
87
  def receiver_task_failure(
119
88
  self,
120
- task_id: Optional[str] = None,
121
- exception: Optional[Exception] = None,
122
- traceback: Optional[TracebackType] = None,
123
- einfo: Optional[Any] = None,
124
- sender: Optional[Type[Any]] = None,
125
- *args: Any,
126
- **kwargs: Any,
127
- ) -> None:
89
+ task_id=None,
90
+ exception=None,
91
+ traceback=None,
92
+ einfo=None,
93
+ sender=None,
94
+ *args,
95
+ **kwargs,
96
+ ):
128
97
  throws = getattr(sender, "throws", ())
129
98
  if isinstance(exception, throws):
130
99
  logger.info(
@@ -139,13 +108,8 @@ class CeleryReceiver:
139
108
  )
140
109
 
141
110
  def receiver_task_revoked(
142
- self,
143
- request: Any,
144
- terminated: Optional[bool] = None,
145
- signum: Optional[Any] = None,
146
- expired: Optional[Any] = None,
147
- **kwargs: Any,
148
- ) -> None:
111
+ self, request=None, terminated=None, signum=None, expired=None, **kwargs
112
+ ):
149
113
  metadata = getattr(request, "__django_structlog__", {}).copy()
150
114
  metadata["task_id"] = request.id
151
115
  metadata["task"] = request.task
@@ -160,31 +124,24 @@ class CeleryReceiver:
160
124
  )
161
125
 
162
126
  def receiver_task_unknown(
163
- self,
164
- message: Optional[str] = None,
165
- exc: Optional[Exception] = None,
166
- name: Optional[str] = None,
167
- id: Optional[str] = None,
168
- **kwargs: Any,
169
- ) -> None:
127
+ self, message=None, exc=None, name=None, id=None, **kwargs
128
+ ):
170
129
  logger.error(
171
130
  "task_not_found",
172
131
  task=name,
173
132
  task_id=id,
174
133
  )
175
134
 
176
- def receiver_task_rejected(
177
- self, message: Any, exc: Optional[Exception] = None, **kwargs: Any
178
- ) -> None:
135
+ def receiver_task_rejected(self, message=None, exc=None, **kwargs):
179
136
  logger.exception(
180
137
  "task_rejected", task_id=message.properties.get("correlation_id")
181
138
  )
182
139
 
183
- def connect_signals(self) -> None:
140
+ def connect_signals(self):
184
141
  before_task_publish.connect(self.receiver_before_task_publish)
185
142
  after_task_publish.connect(self.receiver_after_task_publish)
186
143
 
187
- def connect_worker_signals(self) -> None:
144
+ def connect_worker_signals(self):
188
145
  before_task_publish.connect(self.receiver_before_task_publish)
189
146
  after_task_publish.connect(self.receiver_after_task_publish)
190
147
  task_prerun.connect(self.receiver_task_prerun)
@@ -1,5 +1,3 @@
1
- from typing import Any
2
-
3
1
  from celery import bootsteps
4
2
 
5
3
  from .receivers import CeleryReceiver
@@ -16,7 +14,7 @@ class DjangoStructLogInitStep(bootsteps.Step):
16
14
 
17
15
  """
18
16
 
19
- def __init__(self, parent: Any, **kwargs: Any) -> None:
17
+ def __init__(self, parent, **kwargs):
20
18
  super().__init__(parent, **kwargs)
21
19
  self.receiver = CeleryReceiver()
22
20
  self.receiver.connect_worker_signals()
@@ -1,23 +1,16 @@
1
- from __future__ import annotations
2
-
3
- from typing import List, Tuple, Mapping, Any, Type, TYPE_CHECKING
4
-
5
1
  import structlog
6
2
  import uuid
7
3
 
8
4
  from django_extensions.management.signals import pre_command, post_command
9
5
 
10
- if TYPE_CHECKING:
11
- import contextvars
12
-
13
6
  logger = structlog.getLogger(__name__)
14
7
 
15
8
 
16
9
  class DjangoCommandReceiver:
17
- def __init__(self) -> None:
18
- self.stack: List[Tuple[str, Mapping[str, contextvars.Token[Any]]]] = []
10
+ def __init__(self):
11
+ self.stack = []
19
12
 
20
- def pre_receiver(self, sender: Type[Any], *args: Any, **kwargs: Any) -> None:
13
+ def pre_receiver(self, sender, *args, **kwargs):
21
14
  command_id = str(uuid.uuid4())
22
15
  if len(self.stack):
23
16
  parent_command_id, _ = self.stack[-1]
@@ -33,15 +26,13 @@ class DjangoCommandReceiver:
33
26
  command_name=sender.__module__.replace(".management.commands", ""),
34
27
  )
35
28
 
36
- def post_receiver(
37
- self, sender: Type[Any], outcome: str, *args: Any, **kwargs: Any
38
- ) -> None:
29
+ def post_receiver(self, sender, outcome, *args, **kwargs):
39
30
  logger.info("command_finished")
40
31
 
41
32
  if len(self.stack): # pragma: no branch
42
33
  command_id, tokens = self.stack.pop()
43
34
  structlog.contextvars.reset_contextvars(**tokens)
44
35
 
45
- def connect_signals(self) -> None:
36
+ def connect_signals(self):
46
37
  pre_command.connect(self.pre_receiver)
47
38
  post_command.connect(self.post_receiver)
@@ -1,45 +1,29 @@
1
- from __future__ import annotations
2
-
3
1
  import asyncio
4
2
  import logging
3
+ import sys
5
4
  import uuid
6
- from typing import (
7
- Any,
8
- Generator,
9
- AsyncGenerator,
10
- Callable,
11
- Union,
12
- Awaitable,
13
- cast,
14
- Iterator,
15
- TYPE_CHECKING,
16
- )
17
5
 
18
6
  import structlog
19
7
  from asgiref.sync import iscoroutinefunction, markcoroutinefunction
20
8
  from django.core.exceptions import PermissionDenied
9
+ from django.core.signals import got_request_exception
21
10
  from django.http import Http404, StreamingHttpResponse
22
11
  from asgiref import sync
23
12
 
24
13
  from .. import signals
25
14
  from ..app_settings import app_settings
26
15
 
27
- if TYPE_CHECKING:
28
- from django.http import HttpRequest, HttpResponse
29
-
30
16
  logger = structlog.getLogger(__name__)
31
17
 
32
18
 
33
- def get_request_header(request: HttpRequest, header_key: str, meta_key: str) -> Any:
19
+ def get_request_header(request, header_key, meta_key):
34
20
  if hasattr(request, "headers"):
35
21
  return request.headers.get(header_key)
36
22
 
37
23
  return request.META.get(meta_key)
38
24
 
39
25
 
40
- def sync_streaming_content_wrapper(
41
- streaming_content: Any, context: Any
42
- ) -> Generator[Any, None, None]:
26
+ def sync_streaming_content_wrapper(streaming_content, context):
43
27
  with structlog.contextvars.bound_contextvars(**context):
44
28
  logger.info("streaming_started")
45
29
  try:
@@ -51,9 +35,7 @@ def sync_streaming_content_wrapper(
51
35
  logger.info("streaming_finished")
52
36
 
53
37
 
54
- async def async_streaming_content_wrapper(
55
- streaming_content: Any, context: Any
56
- ) -> AsyncGenerator[Any, None]:
38
+ async def async_streaming_content_wrapper(streaming_content, context):
57
39
  with structlog.contextvars.bound_contextvars(**context):
58
40
  logger.info("streaming_started")
59
41
  try:
@@ -81,37 +63,36 @@ class RequestMiddleware:
81
63
  sync_capable = True
82
64
  async_capable = True
83
65
 
84
- def __init__(
85
- self,
86
- get_response: Callable[
87
- [HttpRequest], Union[HttpResponse, Awaitable[HttpResponse]]
88
- ],
89
- ) -> None:
66
+ def __init__(self, get_response):
90
67
  self.get_response = get_response
91
68
  if iscoroutinefunction(self.get_response):
92
69
  markcoroutinefunction(self)
70
+ got_request_exception.connect(self.process_got_request_exception)
93
71
 
94
- def __call__(
95
- self, request: HttpRequest
96
- ) -> Union[HttpResponse, Awaitable[HttpResponse]]:
72
+ def __call__(self, request):
97
73
  if iscoroutinefunction(self):
98
- return cast(RequestMiddleware, self).__acall__(request) # type: ignore[redundant-cast,unused-ignore]
74
+ return self.__acall__(request)
99
75
  self.prepare(request)
100
- response = cast("HttpResponse", self.get_response(request))
76
+ response = self.get_response(request)
101
77
  self.handle_response(request, response)
102
78
  return response
103
79
 
104
- async def __acall__(self, request: HttpRequest) -> HttpResponse:
80
+ def process_got_request_exception(self, sender, request, **kwargs):
81
+ if not hasattr(request, "_raised_exception"):
82
+ ex = sys.exc_info()
83
+ self.process_exception(request, ex[1])
84
+
85
+ async def __acall__(self, request):
105
86
  await sync.sync_to_async(self.prepare)(request)
106
87
  try:
107
- response = await cast(Awaitable["HttpResponse"], self.get_response(request))
88
+ response = await self.get_response(request)
108
89
  except asyncio.CancelledError:
109
90
  logger.warning("request_cancelled")
110
91
  raise
111
92
  await sync.sync_to_async(self.handle_response)(request, response)
112
93
  return response
113
94
 
114
- def handle_response(self, request: HttpRequest, response: HttpResponse) -> None:
95
+ def handle_response(self, request, response):
115
96
  if not hasattr(request, "_raised_exception"):
116
97
  self.bind_user_id(request)
117
98
  context = structlog.contextvars.get_merged_contextvars(logger)
@@ -141,7 +122,7 @@ class RequestMiddleware:
141
122
  if isinstance(response, StreamingHttpResponse):
142
123
  streaming_content = response.streaming_content
143
124
  try:
144
- iter(cast(Iterator[bytes], streaming_content))
125
+ iter(streaming_content)
145
126
  except TypeError:
146
127
  response.streaming_content = async_streaming_content_wrapper(
147
128
  streaming_content, context
@@ -163,7 +144,7 @@ class RequestMiddleware:
163
144
  )
164
145
  structlog.contextvars.clear_contextvars()
165
146
 
166
- def prepare(self, request: HttpRequest) -> None:
147
+ def prepare(self, request):
167
148
  from ipware import get_client_ip
168
149
 
169
150
  request_id = get_request_header(
@@ -188,11 +169,11 @@ class RequestMiddleware:
188
169
  logger.info("request_started", **log_kwargs)
189
170
 
190
171
  @staticmethod
191
- def format_request(request: HttpRequest) -> str:
172
+ def format_request(request):
192
173
  return f"{request.method} {request.get_full_path()}"
193
174
 
194
175
  @staticmethod
195
- def bind_user_id(request: HttpRequest) -> None:
176
+ def bind_user_id(request):
196
177
  user_id_field = app_settings.USER_ID_FIELD
197
178
  if hasattr(request, "user") and request.user is not None and user_id_field:
198
179
  user_id = None
@@ -202,7 +183,7 @@ class RequestMiddleware:
202
183
  user_id = str(user_id)
203
184
  structlog.contextvars.bind_contextvars(user_id=user_id)
204
185
 
205
- def process_exception(self, request: HttpRequest, exception: Exception) -> None:
186
+ def process_exception(self, request, exception):
206
187
  if isinstance(exception, (Http404, PermissionDenied)):
207
188
  # We don't log an exception here, and we don't set that we handled
208
189
  # an error as we want the standard `request_finished` log message
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: django-structlog
3
- Version: 9.0.0.dev1
3
+ Version: 9.0.0.dev2
4
4
  Summary: Structured Logging for Django
5
5
  Author-email: Jules Robichaud-Gagnon <j.robichaudg+pypi@gmail.com>
6
6
  License: MIT
@@ -24,7 +24,6 @@ Classifier: Programming Language :: Python :: 3.13
24
24
  Classifier: Topic :: System :: Logging
25
25
  Classifier: License :: OSI Approved :: MIT License
26
26
  Classifier: Operating System :: OS Independent
27
- Classifier: Typing :: Typed
28
27
  Requires-Python: >=3.9
29
28
  Description-Content-Type: text/x-rst
30
29
  License-File: LICENSE.rst
@@ -6,7 +6,6 @@ django_structlog/__init__.py
6
6
  django_structlog/app_settings.py
7
7
  django_structlog/apps.py
8
8
  django_structlog/commands.py
9
- django_structlog/py.typed
10
9
  django_structlog/signals.py
11
10
  django_structlog.egg-info/PKG-INFO
12
11
  django_structlog.egg-info/SOURCES.txt
@@ -34,7 +34,6 @@ build-backend = "setuptools.build_meta"
34
34
  "Topic :: System :: Logging",
35
35
  "License :: OSI Approved :: MIT License",
36
36
  "Operating System :: OS Independent",
37
- "Typing :: Typed",
38
37
  ]
39
38
 
40
39
  [project.urls]
@@ -153,22 +152,3 @@ include = [
153
152
  "./django_structlog_demo_project/*",
154
153
  "./test_app/*",
155
154
  ]
156
-
157
- [tool.mypy]
158
- python_version=3.9
159
- strict=true
160
- packages=[
161
- "django_structlog"
162
- ]
163
- disallow_subclassing_any=false
164
-
165
- [[tool.mypy.overrides]]
166
- module = [
167
- "django_extensions.*",
168
- "ipware",
169
- "django.*",
170
- "structlog.*",
171
- "celery.*",
172
- "asgiref.*",
173
- ]
174
- ignore_missing_imports = true
File without changes