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.
- {django_structlog-9.0.1.dev1/django_structlog.egg-info → django_structlog-9.1.0}/PKG-INFO +5 -3
- {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/README.rst +1 -1
- django_structlog-9.1.0/django_structlog/__init__.py +7 -0
- {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/django_structlog/app_settings.py +4 -0
- django_structlog-9.1.0/django_structlog/celery/__init__.py +1 -0
- {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/django_structlog/celery/receivers.py +22 -2
- {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/django_structlog/middlewares/request.py +30 -5
- {django_structlog-9.0.1.dev1 → django_structlog-9.1.0/django_structlog.egg-info}/PKG-INFO +5 -3
- {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/pyproject.toml +5 -3
- django_structlog-9.0.1.dev1/django_structlog/__init__.py +0 -8
- django_structlog-9.0.1.dev1/django_structlog/celery/__init__.py +0 -2
- {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/LICENSE.rst +0 -0
- {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/MANIFEST.in +0 -0
- {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/django_structlog/apps.py +0 -0
- {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/django_structlog/celery/signals.py +0 -0
- {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/django_structlog/celery/steps.py +0 -0
- {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/django_structlog/commands.py +0 -0
- {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/django_structlog/middlewares/__init__.py +0 -0
- {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/django_structlog/py.typed +0 -0
- {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/django_structlog/signals.py +0 -0
- {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/django_structlog.egg-info/SOURCES.txt +0 -0
- {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/django_structlog.egg-info/dependency_links.txt +0 -0
- {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/django_structlog.egg-info/requires.txt +0 -0
- {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/django_structlog.egg-info/top_level.txt +0 -0
- {django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: django-structlog
|
|
3
|
-
Version: 9.
|
|
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
|
|
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
|
|
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
|
|
|
@@ -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
|
-
|
|
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(
|
{django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/django_structlog/middlewares/request.py
RENAMED
|
@@ -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
|
-
|
|
195
|
-
|
|
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")
|
|
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.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: django-structlog
|
|
3
|
-
Version: 9.
|
|
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
|
|
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-
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/django_structlog/middlewares/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/django_structlog.egg-info/SOURCES.txt
RENAMED
|
File without changes
|
|
File without changes
|
{django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/django_structlog.egg-info/requires.txt
RENAMED
|
File without changes
|
{django_structlog-9.0.1.dev1 → django_structlog-9.1.0}/django_structlog.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|