django-structlog 9.0.1.dev1__tar.gz → 9.1.0__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 (25) hide show
  1. {django_structlog-9.0.1.dev1/django_structlog.egg-info → django_structlog-9.1.0}/PKG-INFO +5 -3
  2. {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/README.rst +1 -1
  3. django_structlog-9.1.0/django_structlog/__init__.py +7 -0
  4. {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/django_structlog/app_settings.py +4 -0
  5. django_structlog-9.1.0/django_structlog/celery/__init__.py +1 -0
  6. {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/django_structlog/celery/receivers.py +22 -2
  7. {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/django_structlog/middlewares/request.py +30 -5
  8. {django_structlog-9.0.1.dev1 → django_structlog-9.1.0/django_structlog.egg-info}/PKG-INFO +5 -3
  9. {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/pyproject.toml +5 -3
  10. django_structlog-9.0.1.dev1/django_structlog/__init__.py +0 -8
  11. django_structlog-9.0.1.dev1/django_structlog/celery/__init__.py +0 -2
  12. {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/LICENSE.rst +0 -0
  13. {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/MANIFEST.in +0 -0
  14. {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/django_structlog/apps.py +0 -0
  15. {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/django_structlog/celery/signals.py +0 -0
  16. {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/django_structlog/celery/steps.py +0 -0
  17. {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/django_structlog/commands.py +0 -0
  18. {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/django_structlog/middlewares/__init__.py +0 -0
  19. {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/django_structlog/py.typed +0 -0
  20. {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/django_structlog/signals.py +0 -0
  21. {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/django_structlog.egg-info/SOURCES.txt +0 -0
  22. {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/django_structlog.egg-info/dependency_links.txt +0 -0
  23. {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/django_structlog.egg-info/requires.txt +0 -0
  24. {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/django_structlog.egg-info/top_level.txt +0 -0
  25. {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: django-structlog
3
- Version: 9.0.1.dev1
3
+ Version: 9.1.0
4
4
  Summary: Structured Logging for Django
5
5
  Author-email: Jules Robichaud-Gagnon <j.robichaudg+pypi@gmail.com>
6
6
  License: MIT
@@ -14,6 +14,7 @@ Classifier: Framework :: Django
14
14
  Classifier: Framework :: Django :: 4.2
15
15
  Classifier: Framework :: Django :: 5.0
16
16
  Classifier: Framework :: Django :: 5.1
17
+ Classifier: Framework :: Django :: 5.2
17
18
  Classifier: Programming Language :: Python :: 3
18
19
  Classifier: Programming Language :: Python :: 3 :: Only
19
20
  Classifier: Programming Language :: Python :: 3.9
@@ -36,6 +37,7 @@ Provides-Extra: celery
36
37
  Requires-Dist: celery>=5.1; extra == "celery"
37
38
  Provides-Extra: commands
38
39
  Requires-Dist: django-extensions>=1.4.9; extra == "commands"
40
+ Dynamic: license-file
39
41
 
40
42
  .. inclusion-marker-introduction-begin
41
43
 
@@ -443,7 +445,7 @@ Other libraries alike may be affected by this change.
443
445
  Internal changes in how ``RequestMiddleware`` handles exceptions
444
446
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
445
447
 
446
- This only affects you if you implemented a middleware inheriting from ``RequestMiddleware`` and you overrided the ``process_exception`` method.
448
+ This only affects you if you implemented a middleware inheriting from ``RequestMiddleware`` and you overrode the ``process_exception`` method.
447
449
 
448
450
  Did you?
449
451
 
@@ -404,7 +404,7 @@ Other libraries alike may be affected by this change.
404
404
  Internal changes in how ``RequestMiddleware`` handles exceptions
405
405
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
406
406
 
407
- This only affects you if you implemented a middleware inheriting from ``RequestMiddleware`` and you overrided the ``process_exception`` method.
407
+ This only affects you if you implemented a middleware inheriting from ``RequestMiddleware`` and you overrode the ``process_exception`` method.
408
408
 
409
409
  Did you?
410
410
 
@@ -0,0 +1,7 @@
1
+ """``django-structlog`` is a structured logging integration for ``Django`` project using ``structlog``."""
2
+
3
+ name = "django_structlog"
4
+
5
+ VERSION = (9, 1, 0)
6
+
7
+ __version__ = ".".join(str(v) for v in VERSION)
@@ -11,6 +11,10 @@ class AppSettings:
11
11
  def CELERY_ENABLED(self) -> bool:
12
12
  return getattr(settings, self.PREFIX + "CELERY_ENABLED", False)
13
13
 
14
+ @property
15
+ def IP_LOGGING_ENABLED(self) -> bool:
16
+ return getattr(settings, self.PREFIX + "IP_LOGGING_ENABLED", True)
17
+
14
18
  @property
15
19
  def STATUS_4XX_LOG_LEVEL(self) -> int:
16
20
  return getattr(settings, self.PREFIX + "STATUS_4XX_LOG_LEVEL", logging.WARNING)
@@ -0,0 +1 @@
1
+ """``celery`` integration for ``django_structlog``."""
@@ -1,3 +1,4 @@
1
+ import time
1
2
  from typing import TYPE_CHECKING, Any, Optional, Type, cast
2
3
 
3
4
  import structlog
@@ -93,6 +94,8 @@ class CeleryReceiver:
93
94
  signals.bind_extra_task_metadata.send(
94
95
  sender=self.receiver_task_prerun, task=task, logger=logger
95
96
  )
97
+ # Record the start time so we can log the task duration later.
98
+ task.request._django_structlog_started_at = time.monotonic_ns()
96
99
  logger.info("task_started", task=task.name)
97
100
 
98
101
  def receiver_task_retry(
@@ -105,12 +108,15 @@ class CeleryReceiver:
105
108
  logger.warning("task_retrying", reason=reason)
106
109
 
107
110
  def receiver_task_success(
108
- self, result: Optional[str] = None, **kwargs: Any
111
+ self, result: Optional[str] = None, sender: Optional[Any] = None, **kwargs: Any
109
112
  ) -> None:
110
113
  signals.pre_task_succeeded.send(
111
114
  sender=self.receiver_task_success, logger=logger, result=result
112
115
  )
113
- logger.info("task_succeeded")
116
+
117
+ log_vars: dict[str, Any] = {}
118
+ self.add_duration_ms(sender, log_vars)
119
+ logger.info("task_succeeded", **log_vars)
114
120
 
115
121
  def receiver_task_failure(
116
122
  self,
@@ -122,17 +128,31 @@ class CeleryReceiver:
122
128
  *args: Any,
123
129
  **kwargs: Any,
124
130
  ) -> None:
131
+ log_vars: dict[str, Any] = {}
132
+ self.add_duration_ms(sender, log_vars)
125
133
  throws = getattr(sender, "throws", ())
126
134
  if isinstance(exception, throws):
127
135
  logger.info(
128
136
  "task_failed",
129
137
  error=str(exception),
138
+ **log_vars,
130
139
  )
131
140
  else:
132
141
  logger.exception(
133
142
  "task_failed",
134
143
  error=str(exception),
135
144
  exception=exception,
145
+ **log_vars,
146
+ )
147
+
148
+ @classmethod
149
+ def add_duration_ms(
150
+ cls, task: Optional[Type[Any]], log_vars: dict[str, Any]
151
+ ) -> None:
152
+ if task and hasattr(task, "_django_structlog_started_at"):
153
+ started_at: int = task.request._django_structlog_started_at
154
+ log_vars["duration_ms"] = round(
155
+ (time.monotonic_ns() - started_at) / 1_000_000
136
156
  )
137
157
 
138
158
  def receiver_task_revoked(
@@ -21,6 +21,7 @@ from asgiref import sync
21
21
  from django.core.exceptions import PermissionDenied
22
22
  from django.core.signals import got_request_exception
23
23
  from django.http import Http404, StreamingHttpResponse
24
+ from django.utils.functional import SimpleLazyObject
24
25
 
25
26
  from .. import signals
26
27
  from ..app_settings import app_settings
@@ -39,6 +40,7 @@ else:
39
40
  if TYPE_CHECKING: # pragma: no cover
40
41
  from types import TracebackType
41
42
 
43
+ from django.contrib.auth.base_user import AbstractBaseUser
42
44
  from django.http import HttpRequest, HttpResponse
43
45
 
44
46
  logger = structlog.getLogger(__name__)
@@ -179,8 +181,6 @@ class RequestMiddleware:
179
181
  structlog.contextvars.clear_contextvars()
180
182
 
181
183
  def prepare(self, request: "HttpRequest") -> None:
182
- from ipware import get_client_ip # type: ignore[import-untyped]
183
-
184
184
  request_id = get_request_header(
185
185
  request, "x-request-id", "HTTP_X_REQUEST_ID"
186
186
  ) or str(uuid.uuid4())
@@ -191,8 +191,8 @@ class RequestMiddleware:
191
191
  self.bind_user_id(request)
192
192
  if correlation_id:
193
193
  structlog.contextvars.bind_contextvars(correlation_id=correlation_id)
194
- ip, _ = get_client_ip(request)
195
- structlog.contextvars.bind_contextvars(ip=ip)
194
+ if app_settings.IP_LOGGING_ENABLED:
195
+ self.bind_ip(request)
196
196
  log_kwargs = {
197
197
  "request": self.format_request(request),
198
198
  "user_agent": request.META.get("HTTP_USER_AGENT"),
@@ -202,6 +202,13 @@ class RequestMiddleware:
202
202
  )
203
203
  logger.info("request_started", **log_kwargs)
204
204
 
205
+ @classmethod
206
+ def bind_ip(cls, request: "HttpRequest") -> None:
207
+ from ipware import get_client_ip # type: ignore[import-untyped]
208
+
209
+ ip, _ = get_client_ip(request)
210
+ structlog.contextvars.bind_contextvars(ip=ip)
211
+
205
212
  @staticmethod
206
213
  def format_request(request: "HttpRequest") -> str:
207
214
  return f"{request.method} {request.get_full_path()}"
@@ -209,7 +216,14 @@ class RequestMiddleware:
209
216
  @staticmethod
210
217
  def bind_user_id(request: "HttpRequest") -> None:
211
218
  user_id_field = app_settings.USER_ID_FIELD
212
- if hasattr(request, "user") and request.user is not None and user_id_field:
219
+ if not user_id_field or not hasattr(request, "user"):
220
+ return
221
+
222
+ session_was_accessed = (
223
+ request.session.accessed if hasattr(request, "session") else None
224
+ )
225
+
226
+ if request.user is not None:
213
227
  user_id = None
214
228
  if hasattr(request.user, user_id_field):
215
229
  user_id = getattr(request.user, user_id_field)
@@ -217,6 +231,17 @@ class RequestMiddleware:
217
231
  user_id = str(user_id)
218
232
  structlog.contextvars.bind_contextvars(user_id=user_id)
219
233
 
234
+ if session_was_accessed is False:
235
+ """using SessionMiddleware but user was never accessed, must reset accessed state"""
236
+ user = request.user
237
+
238
+ def get_user() -> Any:
239
+ request.session.accessed = True
240
+ return user
241
+
242
+ request.user = cast("AbstractBaseUser", SimpleLazyObject(get_user))
243
+ request.session.accessed = False
244
+
220
245
  def process_got_request_exception(
221
246
  self, sender: Type[Any], request: "HttpRequest", **kwargs: Any
222
247
  ) -> None:
@@ -1,6 +1,6 @@
1
- Metadata-Version: 2.2
1
+ Metadata-Version: 2.4
2
2
  Name: django-structlog
3
- Version: 9.0.1.dev1
3
+ Version: 9.1.0
4
4
  Summary: Structured Logging for Django
5
5
  Author-email: Jules Robichaud-Gagnon <j.robichaudg+pypi@gmail.com>
6
6
  License: MIT
@@ -14,6 +14,7 @@ Classifier: Framework :: Django
14
14
  Classifier: Framework :: Django :: 4.2
15
15
  Classifier: Framework :: Django :: 5.0
16
16
  Classifier: Framework :: Django :: 5.1
17
+ Classifier: Framework :: Django :: 5.2
17
18
  Classifier: Programming Language :: Python :: 3
18
19
  Classifier: Programming Language :: Python :: 3 :: Only
19
20
  Classifier: Programming Language :: Python :: 3.9
@@ -36,6 +37,7 @@ Provides-Extra: celery
36
37
  Requires-Dist: celery>=5.1; extra == "celery"
37
38
  Provides-Extra: commands
38
39
  Requires-Dist: django-extensions>=1.4.9; extra == "commands"
40
+ Dynamic: license-file
39
41
 
40
42
  .. inclusion-marker-introduction-begin
41
43
 
@@ -443,7 +445,7 @@ Other libraries alike may be affected by this change.
443
445
  Internal changes in how ``RequestMiddleware`` handles exceptions
444
446
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
445
447
 
446
- This only affects you if you implemented a middleware inheriting from ``RequestMiddleware`` and you overrided the ``process_exception`` method.
448
+ This only affects you if you implemented a middleware inheriting from ``RequestMiddleware`` and you overrode the ``process_exception`` method.
447
449
 
448
450
  Did you?
449
451
 
@@ -24,6 +24,7 @@ build-backend = "setuptools.build_meta"
24
24
  "Framework :: Django :: 4.2",
25
25
  "Framework :: Django :: 5.0",
26
26
  "Framework :: Django :: 5.1",
27
+ "Framework :: Django :: 5.2",
27
28
  "Programming Language :: Python :: 3",
28
29
  "Programming Language :: Python :: 3 :: Only",
29
30
  "Programming Language :: Python :: 3.9",
@@ -102,9 +103,9 @@ build-backend = "setuptools.build_meta"
102
103
  # Also, make sure that all python versions used here are included in ./github/worksflows/main.yml
103
104
  envlist =
104
105
  py{39,310,311}-django42-celery5{2,3}-redis{3,4}-kombu5,
105
- py31{0,1}-django5{0,1}-celery5{3,4}-redis4-kombu5,
106
- py312-django{42,50,51}-celery5{3,4}-redis4-kombu5,
107
- py313-django{51}-celery5{3,4}-redis4-kombu5,
106
+ py31{0,1}-django5{0,1,2}-celery5{3,4}-redis4-kombu5,
107
+ py312-django{42,50,51,52}-celery5{3,4}-redis4-kombu5,
108
+ py313-django5{1,2}-celery5{3,4}-redis4-kombu5,
108
109
 
109
110
  [gh-actions]
110
111
  python =
@@ -132,6 +133,7 @@ build-backend = "setuptools.build_meta"
132
133
  django42: Django >=4.2, <5.0
133
134
  django50: Django >=5.0, <5.1
134
135
  django51: Django >=5.1, <5.2
136
+ django52: Django >=5.2, <6.0
135
137
  -r{toxinidir}/requirements/ci.txt
136
138
 
137
139
  commands = pytest --cov=./test_app --cov=./django_structlog --cov-append test_app
@@ -1,8 +0,0 @@
1
- """ ``django-structlog`` is a structured logging integration for ``Django`` project using ``structlog``.
2
- """
3
-
4
- name = "django_structlog"
5
-
6
- VERSION = (9, 0, 1, "dev1")
7
-
8
- __version__ = ".".join(str(v) for v in VERSION)
@@ -1,2 +0,0 @@
1
- """ ``celery`` integration for ``django_structlog``.
2
- """