django-structlog 9.0.0.dev2__py3-none-any.whl → 9.0.1.dev1__py3-none-any.whl
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/__init__.py +1 -1
- django_structlog/app_settings.py +4 -4
- django_structlog/apps.py +1 -1
- django_structlog/celery/receivers.py +76 -36
- django_structlog/celery/signals.py +0 -1
- django_structlog/celery/steps.py +3 -1
- django_structlog/commands.py +17 -6
- django_structlog/middlewares/__init__.py +4 -0
- django_structlog/middlewares/request.py +71 -27
- django_structlog/py.typed +0 -0
- django_structlog/signals.py +0 -1
- {django_structlog-9.0.0.dev2.dist-info → django_structlog-9.0.1.dev1.dist-info}/METADATA +47 -2
- django_structlog-9.0.1.dev1.dist-info/RECORD +17 -0
- {django_structlog-9.0.0.dev2.dist-info → django_structlog-9.0.1.dev1.dist-info}/WHEEL +1 -1
- django_structlog-9.0.0.dev2.dist-info/RECORD +0 -16
- {django_structlog-9.0.0.dev2.dist-info → django_structlog-9.0.1.dev1.dist-info}/LICENSE.rst +0 -0
- {django_structlog-9.0.0.dev2.dist-info → django_structlog-9.0.1.dev1.dist-info}/top_level.txt +0 -0
django_structlog/__init__.py
CHANGED
django_structlog/app_settings.py
CHANGED
|
@@ -8,19 +8,19 @@ class AppSettings:
|
|
|
8
8
|
PREFIX = "DJANGO_STRUCTLOG_"
|
|
9
9
|
|
|
10
10
|
@property
|
|
11
|
-
def CELERY_ENABLED(self):
|
|
11
|
+
def CELERY_ENABLED(self) -> bool:
|
|
12
12
|
return getattr(settings, self.PREFIX + "CELERY_ENABLED", False)
|
|
13
13
|
|
|
14
14
|
@property
|
|
15
|
-
def STATUS_4XX_LOG_LEVEL(self):
|
|
15
|
+
def STATUS_4XX_LOG_LEVEL(self) -> int:
|
|
16
16
|
return getattr(settings, self.PREFIX + "STATUS_4XX_LOG_LEVEL", logging.WARNING)
|
|
17
17
|
|
|
18
18
|
@property
|
|
19
|
-
def COMMAND_LOGGING_ENABLED(self):
|
|
19
|
+
def COMMAND_LOGGING_ENABLED(self) -> bool:
|
|
20
20
|
return getattr(settings, self.PREFIX + "COMMAND_LOGGING_ENABLED", False)
|
|
21
21
|
|
|
22
22
|
@property
|
|
23
|
-
def USER_ID_FIELD(self):
|
|
23
|
+
def USER_ID_FIELD(self) -> str:
|
|
24
24
|
return getattr(settings, self.PREFIX + "USER_ID_FIELD", "pk")
|
|
25
25
|
|
|
26
26
|
|
django_structlog/apps.py
CHANGED
|
@@ -1,36 +1,42 @@
|
|
|
1
|
+
from typing import TYPE_CHECKING, Any, Optional, Type, cast
|
|
2
|
+
|
|
1
3
|
import structlog
|
|
2
4
|
from celery import current_app
|
|
3
5
|
from celery.signals import (
|
|
4
|
-
before_task_publish,
|
|
5
6
|
after_task_publish,
|
|
7
|
+
before_task_publish,
|
|
8
|
+
task_failure,
|
|
6
9
|
task_prerun,
|
|
10
|
+
task_rejected,
|
|
7
11
|
task_retry,
|
|
8
|
-
task_success,
|
|
9
|
-
task_failure,
|
|
10
12
|
task_revoked,
|
|
13
|
+
task_success,
|
|
11
14
|
task_unknown,
|
|
12
|
-
task_rejected,
|
|
13
15
|
)
|
|
14
16
|
|
|
15
17
|
from . import signals
|
|
16
18
|
|
|
19
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
20
|
+
from types import TracebackType
|
|
17
21
|
|
|
18
22
|
logger = structlog.getLogger(__name__)
|
|
19
23
|
|
|
20
24
|
|
|
21
25
|
class CeleryReceiver:
|
|
22
|
-
|
|
26
|
+
_priority: Optional[str]
|
|
27
|
+
|
|
28
|
+
def __init__(self) -> None:
|
|
23
29
|
self._priority = None
|
|
24
30
|
|
|
25
31
|
def receiver_before_task_publish(
|
|
26
32
|
self,
|
|
27
|
-
sender=None,
|
|
28
|
-
headers=None,
|
|
29
|
-
body=None,
|
|
30
|
-
properties=None,
|
|
31
|
-
routing_key=None,
|
|
32
|
-
**kwargs,
|
|
33
|
-
):
|
|
33
|
+
sender: Optional[Type[Any]] = None,
|
|
34
|
+
headers: Optional[dict[str, Any]] = None,
|
|
35
|
+
body: Optional[dict[str, str]] = None,
|
|
36
|
+
properties: Optional[dict[str, Any]] = None,
|
|
37
|
+
routing_key: Optional[str] = None,
|
|
38
|
+
**kwargs: dict[str, str],
|
|
39
|
+
) -> None:
|
|
34
40
|
if current_app.conf.task_protocol < 2:
|
|
35
41
|
return
|
|
36
42
|
|
|
@@ -46,12 +52,16 @@ class CeleryReceiver:
|
|
|
46
52
|
)
|
|
47
53
|
if properties:
|
|
48
54
|
self._priority = properties.get("priority", None)
|
|
49
|
-
|
|
50
|
-
headers["__django_structlog__"] = context
|
|
55
|
+
cast(dict[str, Any], headers)["__django_structlog__"] = context
|
|
51
56
|
|
|
52
57
|
def receiver_after_task_publish(
|
|
53
|
-
self,
|
|
54
|
-
|
|
58
|
+
self,
|
|
59
|
+
sender: Optional[Type[Any]] = None,
|
|
60
|
+
headers: Optional[dict[str, Optional[str]]] = None,
|
|
61
|
+
body: Optional[dict[str, Optional[str]]] = None,
|
|
62
|
+
routing_key: Optional[str] = None,
|
|
63
|
+
**kwargs: Any,
|
|
64
|
+
) -> None:
|
|
55
65
|
properties = {}
|
|
56
66
|
if self._priority is not None:
|
|
57
67
|
properties["priority"] = self._priority
|
|
@@ -59,13 +69,23 @@ class CeleryReceiver:
|
|
|
59
69
|
|
|
60
70
|
logger.info(
|
|
61
71
|
"task_enqueued",
|
|
62
|
-
child_task_id=
|
|
63
|
-
|
|
72
|
+
child_task_id=(
|
|
73
|
+
headers.get("id")
|
|
74
|
+
if headers
|
|
75
|
+
else cast(dict[str, Optional[str]], body).get("id")
|
|
76
|
+
),
|
|
77
|
+
child_task_name=(
|
|
78
|
+
headers.get("task")
|
|
79
|
+
if headers
|
|
80
|
+
else cast(dict[str, Optional[str]], body).get("task")
|
|
81
|
+
),
|
|
64
82
|
routing_key=routing_key,
|
|
65
83
|
**properties,
|
|
66
84
|
)
|
|
67
85
|
|
|
68
|
-
def receiver_task_prerun(
|
|
86
|
+
def receiver_task_prerun(
|
|
87
|
+
self, task_id: str, task: Any, *args: Any, **kwargs: Any
|
|
88
|
+
) -> None:
|
|
69
89
|
structlog.contextvars.clear_contextvars()
|
|
70
90
|
structlog.contextvars.bind_contextvars(task_id=task_id)
|
|
71
91
|
metadata = getattr(task.request, "__django_structlog__", {})
|
|
@@ -75,10 +95,18 @@ class CeleryReceiver:
|
|
|
75
95
|
)
|
|
76
96
|
logger.info("task_started", task=task.name)
|
|
77
97
|
|
|
78
|
-
def receiver_task_retry(
|
|
98
|
+
def receiver_task_retry(
|
|
99
|
+
self,
|
|
100
|
+
request: Optional[Any] = None,
|
|
101
|
+
reason: Optional[str] = None,
|
|
102
|
+
einfo: Optional[Any] = None,
|
|
103
|
+
**kwargs: Any,
|
|
104
|
+
) -> None:
|
|
79
105
|
logger.warning("task_retrying", reason=reason)
|
|
80
106
|
|
|
81
|
-
def receiver_task_success(
|
|
107
|
+
def receiver_task_success(
|
|
108
|
+
self, result: Optional[str] = None, **kwargs: Any
|
|
109
|
+
) -> None:
|
|
82
110
|
signals.pre_task_succeeded.send(
|
|
83
111
|
sender=self.receiver_task_success, logger=logger, result=result
|
|
84
112
|
)
|
|
@@ -86,14 +114,14 @@ class CeleryReceiver:
|
|
|
86
114
|
|
|
87
115
|
def receiver_task_failure(
|
|
88
116
|
self,
|
|
89
|
-
task_id=None,
|
|
90
|
-
exception=None,
|
|
91
|
-
traceback=None,
|
|
92
|
-
einfo=None,
|
|
93
|
-
sender=None,
|
|
94
|
-
*args,
|
|
95
|
-
**kwargs,
|
|
96
|
-
):
|
|
117
|
+
task_id: Optional[str] = None,
|
|
118
|
+
exception: Optional[Exception] = None,
|
|
119
|
+
traceback: Optional["TracebackType"] = None,
|
|
120
|
+
einfo: Optional[Any] = None,
|
|
121
|
+
sender: Optional[Type[Any]] = None,
|
|
122
|
+
*args: Any,
|
|
123
|
+
**kwargs: Any,
|
|
124
|
+
) -> None:
|
|
97
125
|
throws = getattr(sender, "throws", ())
|
|
98
126
|
if isinstance(exception, throws):
|
|
99
127
|
logger.info(
|
|
@@ -108,8 +136,13 @@ class CeleryReceiver:
|
|
|
108
136
|
)
|
|
109
137
|
|
|
110
138
|
def receiver_task_revoked(
|
|
111
|
-
self,
|
|
112
|
-
|
|
139
|
+
self,
|
|
140
|
+
request: Any,
|
|
141
|
+
terminated: Optional[bool] = None,
|
|
142
|
+
signum: Optional[Any] = None,
|
|
143
|
+
expired: Optional[Any] = None,
|
|
144
|
+
**kwargs: Any,
|
|
145
|
+
) -> None:
|
|
113
146
|
metadata = getattr(request, "__django_structlog__", {}).copy()
|
|
114
147
|
metadata["task_id"] = request.id
|
|
115
148
|
metadata["task"] = request.task
|
|
@@ -124,24 +157,31 @@ class CeleryReceiver:
|
|
|
124
157
|
)
|
|
125
158
|
|
|
126
159
|
def receiver_task_unknown(
|
|
127
|
-
self,
|
|
128
|
-
|
|
160
|
+
self,
|
|
161
|
+
message: Optional[str] = None,
|
|
162
|
+
exc: Optional[Exception] = None,
|
|
163
|
+
name: Optional[str] = None,
|
|
164
|
+
id: Optional[str] = None,
|
|
165
|
+
**kwargs: Any,
|
|
166
|
+
) -> None:
|
|
129
167
|
logger.error(
|
|
130
168
|
"task_not_found",
|
|
131
169
|
task=name,
|
|
132
170
|
task_id=id,
|
|
133
171
|
)
|
|
134
172
|
|
|
135
|
-
def receiver_task_rejected(
|
|
173
|
+
def receiver_task_rejected(
|
|
174
|
+
self, message: Any, exc: Optional[Exception] = None, **kwargs: Any
|
|
175
|
+
) -> None:
|
|
136
176
|
logger.exception(
|
|
137
177
|
"task_rejected", task_id=message.properties.get("correlation_id")
|
|
138
178
|
)
|
|
139
179
|
|
|
140
|
-
def connect_signals(self):
|
|
180
|
+
def connect_signals(self) -> None:
|
|
141
181
|
before_task_publish.connect(self.receiver_before_task_publish)
|
|
142
182
|
after_task_publish.connect(self.receiver_after_task_publish)
|
|
143
183
|
|
|
144
|
-
def connect_worker_signals(self):
|
|
184
|
+
def connect_worker_signals(self) -> None:
|
|
145
185
|
before_task_publish.connect(self.receiver_before_task_publish)
|
|
146
186
|
after_task_publish.connect(self.receiver_after_task_publish)
|
|
147
187
|
task_prerun.connect(self.receiver_task_prerun)
|
django_structlog/celery/steps.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
1
3
|
from celery import bootsteps
|
|
2
4
|
|
|
3
5
|
from .receivers import CeleryReceiver
|
|
@@ -14,7 +16,7 @@ class DjangoStructLogInitStep(bootsteps.Step):
|
|
|
14
16
|
|
|
15
17
|
"""
|
|
16
18
|
|
|
17
|
-
def __init__(self, parent, **kwargs):
|
|
19
|
+
def __init__(self, parent: Any, **kwargs: Any) -> None:
|
|
18
20
|
super().__init__(parent, **kwargs)
|
|
19
21
|
self.receiver = CeleryReceiver()
|
|
20
22
|
self.receiver.connect_worker_signals()
|
django_structlog/commands.py
CHANGED
|
@@ -1,16 +1,25 @@
|
|
|
1
|
-
import structlog
|
|
2
1
|
import uuid
|
|
2
|
+
from typing import TYPE_CHECKING, Any, List, Mapping, Tuple, Type
|
|
3
|
+
|
|
4
|
+
import structlog
|
|
5
|
+
from django_extensions.management.signals import ( # type: ignore[import-untyped]
|
|
6
|
+
post_command,
|
|
7
|
+
pre_command,
|
|
8
|
+
)
|
|
3
9
|
|
|
4
|
-
|
|
10
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
11
|
+
import contextvars
|
|
5
12
|
|
|
6
13
|
logger = structlog.getLogger(__name__)
|
|
7
14
|
|
|
8
15
|
|
|
9
16
|
class DjangoCommandReceiver:
|
|
10
|
-
|
|
17
|
+
stack: List[Tuple[str, Mapping[str, "contextvars.Token[Any]"]]]
|
|
18
|
+
|
|
19
|
+
def __init__(self) -> None:
|
|
11
20
|
self.stack = []
|
|
12
21
|
|
|
13
|
-
def pre_receiver(self, sender, *args, **kwargs):
|
|
22
|
+
def pre_receiver(self, sender: Type[Any], *args: Any, **kwargs: Any) -> None:
|
|
14
23
|
command_id = str(uuid.uuid4())
|
|
15
24
|
if len(self.stack):
|
|
16
25
|
parent_command_id, _ = self.stack[-1]
|
|
@@ -26,13 +35,15 @@ class DjangoCommandReceiver:
|
|
|
26
35
|
command_name=sender.__module__.replace(".management.commands", ""),
|
|
27
36
|
)
|
|
28
37
|
|
|
29
|
-
def post_receiver(
|
|
38
|
+
def post_receiver(
|
|
39
|
+
self, sender: Type[Any], outcome: str, *args: Any, **kwargs: Any
|
|
40
|
+
) -> None:
|
|
30
41
|
logger.info("command_finished")
|
|
31
42
|
|
|
32
43
|
if len(self.stack): # pragma: no branch
|
|
33
44
|
command_id, tokens = self.stack.pop()
|
|
34
45
|
structlog.contextvars.reset_contextvars(**tokens)
|
|
35
46
|
|
|
36
|
-
def connect_signals(self):
|
|
47
|
+
def connect_signals(self) -> None:
|
|
37
48
|
pre_command.connect(self.pre_receiver)
|
|
38
49
|
post_command.connect(self.post_receiver)
|
|
@@ -2,28 +2,58 @@ import asyncio
|
|
|
2
2
|
import logging
|
|
3
3
|
import sys
|
|
4
4
|
import uuid
|
|
5
|
+
from typing import (
|
|
6
|
+
TYPE_CHECKING,
|
|
7
|
+
Any,
|
|
8
|
+
AsyncGenerator,
|
|
9
|
+
AsyncIterator,
|
|
10
|
+
Awaitable,
|
|
11
|
+
Callable,
|
|
12
|
+
Generator,
|
|
13
|
+
Iterator,
|
|
14
|
+
Type,
|
|
15
|
+
Union,
|
|
16
|
+
cast,
|
|
17
|
+
)
|
|
5
18
|
|
|
6
19
|
import structlog
|
|
7
|
-
from asgiref
|
|
20
|
+
from asgiref import sync
|
|
8
21
|
from django.core.exceptions import PermissionDenied
|
|
9
22
|
from django.core.signals import got_request_exception
|
|
10
23
|
from django.http import Http404, StreamingHttpResponse
|
|
11
|
-
from asgiref import sync
|
|
12
24
|
|
|
13
25
|
from .. import signals
|
|
14
26
|
from ..app_settings import app_settings
|
|
15
27
|
|
|
28
|
+
if sys.version_info >= (3, 12, 0):
|
|
29
|
+
from inspect import ( # type: ignore[attr-defined]
|
|
30
|
+
iscoroutinefunction,
|
|
31
|
+
markcoroutinefunction,
|
|
32
|
+
)
|
|
33
|
+
else:
|
|
34
|
+
from asgiref.sync import ( # type: ignore[no-redef]
|
|
35
|
+
iscoroutinefunction,
|
|
36
|
+
markcoroutinefunction,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
if TYPE_CHECKING: # pragma: no cover
|
|
40
|
+
from types import TracebackType
|
|
41
|
+
|
|
42
|
+
from django.http import HttpRequest, HttpResponse
|
|
43
|
+
|
|
16
44
|
logger = structlog.getLogger(__name__)
|
|
17
45
|
|
|
18
46
|
|
|
19
|
-
def get_request_header(request, header_key, meta_key):
|
|
47
|
+
def get_request_header(request: "HttpRequest", header_key: str, meta_key: str) -> Any:
|
|
20
48
|
if hasattr(request, "headers"):
|
|
21
49
|
return request.headers.get(header_key)
|
|
22
50
|
|
|
23
51
|
return request.META.get(meta_key)
|
|
24
52
|
|
|
25
53
|
|
|
26
|
-
def sync_streaming_content_wrapper(
|
|
54
|
+
def sync_streaming_content_wrapper(
|
|
55
|
+
streaming_content: Iterator[bytes], context: Any
|
|
56
|
+
) -> Generator[bytes, None, None]:
|
|
27
57
|
with structlog.contextvars.bound_contextvars(**context):
|
|
28
58
|
logger.info("streaming_started")
|
|
29
59
|
try:
|
|
@@ -31,11 +61,14 @@ def sync_streaming_content_wrapper(streaming_content, context):
|
|
|
31
61
|
yield chunk
|
|
32
62
|
except Exception:
|
|
33
63
|
logger.exception("streaming_failed")
|
|
64
|
+
raise
|
|
34
65
|
else:
|
|
35
66
|
logger.info("streaming_finished")
|
|
36
67
|
|
|
37
68
|
|
|
38
|
-
async def async_streaming_content_wrapper(
|
|
69
|
+
async def async_streaming_content_wrapper(
|
|
70
|
+
streaming_content: AsyncIterator[bytes], context: Any
|
|
71
|
+
) -> AsyncGenerator[bytes, Any]:
|
|
39
72
|
with structlog.contextvars.bound_contextvars(**context):
|
|
40
73
|
logger.info("streaming_started")
|
|
41
74
|
try:
|
|
@@ -46,6 +79,7 @@ async def async_streaming_content_wrapper(streaming_content, context):
|
|
|
46
79
|
raise
|
|
47
80
|
except Exception:
|
|
48
81
|
logger.exception("streaming_failed")
|
|
82
|
+
raise
|
|
49
83
|
else:
|
|
50
84
|
logger.info("streaming_finished")
|
|
51
85
|
|
|
@@ -63,36 +97,38 @@ class RequestMiddleware:
|
|
|
63
97
|
sync_capable = True
|
|
64
98
|
async_capable = True
|
|
65
99
|
|
|
66
|
-
def __init__(
|
|
100
|
+
def __init__(
|
|
101
|
+
self,
|
|
102
|
+
get_response: Callable[
|
|
103
|
+
["HttpRequest"], Union["HttpResponse", Awaitable["HttpResponse"]]
|
|
104
|
+
],
|
|
105
|
+
) -> None:
|
|
67
106
|
self.get_response = get_response
|
|
68
107
|
if iscoroutinefunction(self.get_response):
|
|
69
108
|
markcoroutinefunction(self)
|
|
70
109
|
got_request_exception.connect(self.process_got_request_exception)
|
|
71
110
|
|
|
72
|
-
def __call__(
|
|
111
|
+
def __call__(
|
|
112
|
+
self, request: "HttpRequest"
|
|
113
|
+
) -> Union["HttpResponse", Awaitable["HttpResponse"]]:
|
|
73
114
|
if iscoroutinefunction(self):
|
|
74
|
-
return self.__acall__(request)
|
|
115
|
+
return cast(RequestMiddleware, self).__acall__(request)
|
|
75
116
|
self.prepare(request)
|
|
76
|
-
response = self.get_response(request)
|
|
117
|
+
response = cast("HttpResponse", self.get_response(request))
|
|
77
118
|
self.handle_response(request, response)
|
|
78
119
|
return response
|
|
79
120
|
|
|
80
|
-
def
|
|
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):
|
|
121
|
+
async def __acall__(self, request: "HttpRequest") -> "HttpResponse":
|
|
86
122
|
await sync.sync_to_async(self.prepare)(request)
|
|
87
123
|
try:
|
|
88
|
-
response = await self.get_response(request)
|
|
124
|
+
response = await cast(Awaitable["HttpResponse"], self.get_response(request))
|
|
89
125
|
except asyncio.CancelledError:
|
|
90
126
|
logger.warning("request_cancelled")
|
|
91
127
|
raise
|
|
92
128
|
await sync.sync_to_async(self.handle_response)(request, response)
|
|
93
129
|
return response
|
|
94
130
|
|
|
95
|
-
def handle_response(self, request, response):
|
|
131
|
+
def handle_response(self, request: "HttpRequest", response: "HttpResponse") -> None:
|
|
96
132
|
if not hasattr(request, "_raised_exception"):
|
|
97
133
|
self.bind_user_id(request)
|
|
98
134
|
context = structlog.contextvars.get_merged_contextvars(logger)
|
|
@@ -121,15 +157,13 @@ class RequestMiddleware:
|
|
|
121
157
|
)
|
|
122
158
|
if isinstance(response, StreamingHttpResponse):
|
|
123
159
|
streaming_content = response.streaming_content
|
|
124
|
-
|
|
125
|
-
iter(streaming_content)
|
|
126
|
-
except TypeError:
|
|
160
|
+
if response.is_async:
|
|
127
161
|
response.streaming_content = async_streaming_content_wrapper(
|
|
128
|
-
streaming_content, context
|
|
162
|
+
cast(AsyncIterator[bytes], streaming_content), context
|
|
129
163
|
)
|
|
130
164
|
else:
|
|
131
165
|
response.streaming_content = sync_streaming_content_wrapper(
|
|
132
|
-
streaming_content, context
|
|
166
|
+
cast(Iterator[bytes], streaming_content), context
|
|
133
167
|
)
|
|
134
168
|
|
|
135
169
|
else:
|
|
@@ -144,8 +178,8 @@ class RequestMiddleware:
|
|
|
144
178
|
)
|
|
145
179
|
structlog.contextvars.clear_contextvars()
|
|
146
180
|
|
|
147
|
-
def prepare(self, request):
|
|
148
|
-
from ipware import get_client_ip
|
|
181
|
+
def prepare(self, request: "HttpRequest") -> None:
|
|
182
|
+
from ipware import get_client_ip # type: ignore[import-untyped]
|
|
149
183
|
|
|
150
184
|
request_id = get_request_header(
|
|
151
185
|
request, "x-request-id", "HTTP_X_REQUEST_ID"
|
|
@@ -169,11 +203,11 @@ class RequestMiddleware:
|
|
|
169
203
|
logger.info("request_started", **log_kwargs)
|
|
170
204
|
|
|
171
205
|
@staticmethod
|
|
172
|
-
def format_request(request):
|
|
206
|
+
def format_request(request: "HttpRequest") -> str:
|
|
173
207
|
return f"{request.method} {request.get_full_path()}"
|
|
174
208
|
|
|
175
209
|
@staticmethod
|
|
176
|
-
def bind_user_id(request):
|
|
210
|
+
def bind_user_id(request: "HttpRequest") -> None:
|
|
177
211
|
user_id_field = app_settings.USER_ID_FIELD
|
|
178
212
|
if hasattr(request, "user") and request.user is not None and user_id_field:
|
|
179
213
|
user_id = None
|
|
@@ -183,7 +217,17 @@ class RequestMiddleware:
|
|
|
183
217
|
user_id = str(user_id)
|
|
184
218
|
structlog.contextvars.bind_contextvars(user_id=user_id)
|
|
185
219
|
|
|
186
|
-
def
|
|
220
|
+
def process_got_request_exception(
|
|
221
|
+
self, sender: Type[Any], request: "HttpRequest", **kwargs: Any
|
|
222
|
+
) -> None:
|
|
223
|
+
if not hasattr(request, "_raised_exception"):
|
|
224
|
+
ex = cast(
|
|
225
|
+
tuple[Type[Exception], Exception, "TracebackType"],
|
|
226
|
+
sys.exc_info(),
|
|
227
|
+
)
|
|
228
|
+
self._process_exception(request, ex[1])
|
|
229
|
+
|
|
230
|
+
def _process_exception(self, request: "HttpRequest", exception: Exception) -> None:
|
|
187
231
|
if isinstance(exception, (Http404, PermissionDenied)):
|
|
188
232
|
# We don't log an exception here, and we don't set that we handled
|
|
189
233
|
# an error as we want the standard `request_finished` log message
|
|
File without changes
|
django_structlog/signals.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.2
|
|
2
2
|
Name: django-structlog
|
|
3
|
-
Version: 9.0.
|
|
3
|
+
Version: 9.0.1.dev1
|
|
4
4
|
Summary: Structured Logging for Django
|
|
5
5
|
Author-email: Jules Robichaud-Gagnon <j.robichaudg+pypi@gmail.com>
|
|
6
6
|
License: MIT
|
|
@@ -24,6 +24,7 @@ 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
|
|
27
28
|
Requires-Python: >=3.9
|
|
28
29
|
Description-Content-Type: text/x-rst
|
|
29
30
|
License-File: LICENSE.rst
|
|
@@ -43,8 +44,14 @@ django-structlog
|
|
|
43
44
|
|
|
44
45
|
| |pypi| |wheels| |build-status| |docs| |coverage| |open_issues| |pull_requests|
|
|
45
46
|
| |django| |python| |license| |black| |ruff|
|
|
47
|
+
| |django_packages|
|
|
46
48
|
| |watchers| |stars| |forks|
|
|
47
49
|
|
|
50
|
+
|
|
51
|
+
.. |django_packages| image:: https://img.shields.io/badge/Published%20on-Django%20Packages-0c3c26
|
|
52
|
+
:target: https://djangopackages.org/packages/p/django-structlog/
|
|
53
|
+
:alt: Published on Django Packages
|
|
54
|
+
|
|
48
55
|
.. |build-status| image:: https://github.com/jrobichaud/django-structlog/actions/workflows/main.yml/badge.svg?branch=main
|
|
49
56
|
:target: https://github.com/jrobichaud/django-structlog/actions
|
|
50
57
|
:alt: Build Status
|
|
@@ -407,6 +414,44 @@ Json file (\ ``logs/json.log``\ )
|
|
|
407
414
|
Upgrade Guide
|
|
408
415
|
=============
|
|
409
416
|
|
|
417
|
+
.. _upgrade_9.0:
|
|
418
|
+
|
|
419
|
+
Upgrading to 9.0+
|
|
420
|
+
^^^^^^^^^^^^^^^^^
|
|
421
|
+
|
|
422
|
+
Minimum requirements
|
|
423
|
+
~~~~~~~~~~~~~~~~~~~~
|
|
424
|
+
- requires python 3.9+
|
|
425
|
+
- django 4.2 and 5.0+ are supported
|
|
426
|
+
|
|
427
|
+
|
|
428
|
+
Type hints
|
|
429
|
+
~~~~~~~~~~
|
|
430
|
+
|
|
431
|
+
``django-structlog`` now uses `python type hints <https://docs.python.org/3/library/typing.html>`_ and is being validated with `mypy <https://mypy.readthedocs.io/en/stable/>`_ ``--strict``.
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
For ``drf-standardized-errors`` users
|
|
435
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
436
|
+
|
|
437
|
+
Now unhandled exceptions when using `drf-standardized-errors <https://github.com/ghazi-git/drf-standardized-errors>`_ will be intercepted and the exception logged properly.
|
|
438
|
+
|
|
439
|
+
If you also use `structlog-sentry <https://github.com/kiwicom/structlog-sentry>`_, the exception will now be propagated as expected.
|
|
440
|
+
|
|
441
|
+
Other libraries alike may be affected by this change.
|
|
442
|
+
|
|
443
|
+
Internal changes in how ``RequestMiddleware`` handles exceptions
|
|
444
|
+
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
445
|
+
|
|
446
|
+
This only affects you if you implemented a middleware inheriting from ``RequestMiddleware`` and you overrided the ``process_exception`` method.
|
|
447
|
+
|
|
448
|
+
Did you?
|
|
449
|
+
|
|
450
|
+
If so:
|
|
451
|
+
|
|
452
|
+
- ``RequestMiddleware.process_exception`` was renamed to ``RequestMiddleware._process_exception``, you should to the same in the middleware.
|
|
453
|
+
|
|
454
|
+
|
|
410
455
|
.. _upgrade_8.0:
|
|
411
456
|
|
|
412
457
|
Upgrading to 8.0+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
django_structlog/__init__.py,sha256=PD9sNCVA1rbswModeXBrqYVsweBpQ3eNqDXAzp801fI,214
|
|
2
|
+
django_structlog/app_settings.py,sha256=yf1wYt_H_RL7pwBXgmCqblJuRxGbpve3YoWhlfkYA3A,697
|
|
3
|
+
django_structlog/apps.py,sha256=Ntfsd0xD09tVIOk6AnqUkc-Nl0Z2LJjmKC9u_xTIQ9c,614
|
|
4
|
+
django_structlog/commands.py,sha256=-TIHdwneBgamy4RNOMdG1gtR-wZ5ClJH-36iNlnbeEw,1562
|
|
5
|
+
django_structlog/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
6
|
+
django_structlog/signals.py,sha256=T9YDBG-NDiaqntFbFukTeoWRXLr9k_Foe12WrDMTC4M,3161
|
|
7
|
+
django_structlog/celery/__init__.py,sha256=6KuRCmnb49FOMkJGtrZnx8qgrj4pYLBgNraz_NIJ7KE,57
|
|
8
|
+
django_structlog/celery/receivers.py,sha256=-pxO0yEf6uNmPA0NvlVIc41VraRm3MQ3CjEKlZg8YFA,6187
|
|
9
|
+
django_structlog/celery/signals.py,sha256=CN40ApldyRU6ArbFpn13MlGoR7aFIzMGZ0WVPbXscXg,2115
|
|
10
|
+
django_structlog/celery/steps.py,sha256=9ipqdEc6T_J0R0PPR961x0uAi8bI1RfuFuNLvp4DVxo,641
|
|
11
|
+
django_structlog/middlewares/__init__.py,sha256=Csed6IUMq9SvydoDj1WJ9_o7B49z2qiPR9FUxnVwCco,92
|
|
12
|
+
django_structlog/middlewares/request.py,sha256=cDe6VwkT699YkWtU8Tc5K4gbmPHITzVwvTn3k_FFJIk,8637
|
|
13
|
+
django_structlog-9.0.1.dev1.dist-info/LICENSE.rst,sha256=FmQJJGUr0CAup1Q81JpUuEGsjZM8PCBiNZUuoyHgtBQ,1079
|
|
14
|
+
django_structlog-9.0.1.dev1.dist-info/METADATA,sha256=A_JGRJa60LJ_l3lhYPicfUeAmxJ6yQcBEo6v5rjXaYc,29131
|
|
15
|
+
django_structlog-9.0.1.dev1.dist-info/WHEEL,sha256=In9FTNxeP60KnTkGw7wk6mJPYd_dQSjEZmXdBdMCI-8,91
|
|
16
|
+
django_structlog-9.0.1.dev1.dist-info/top_level.txt,sha256=tGM_7H0Zgu3x7zaOXRkkZx-i18y6fJE9TJi5RhaYd0Y,17
|
|
17
|
+
django_structlog-9.0.1.dev1.dist-info/RECORD,,
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
django_structlog/__init__.py,sha256=my3aISCn2ONUTxtGIsjEH-bpnuTnxcZ7LHFiNgK1fcg,214
|
|
2
|
-
django_structlog/app_settings.py,sha256=-tfrpUhubJAkkuqpYEWUvf5wLjxirudKe2qcWIZpkiM,667
|
|
3
|
-
django_structlog/apps.py,sha256=mVi5b1mIJnVrZRC6UtbxWlpMN2hohVP1olomLELqrAM,606
|
|
4
|
-
django_structlog/commands.py,sha256=bJwosyO-XXVpx_nFEemIGVceL-6OJ0mI8g5FqLrRMpY,1227
|
|
5
|
-
django_structlog/signals.py,sha256=EHy_HMnclfJYJ9gUaJ1kBIvNr9Q2CRZnCDtSi7otVCY,3162
|
|
6
|
-
django_structlog/celery/__init__.py,sha256=6KuRCmnb49FOMkJGtrZnx8qgrj4pYLBgNraz_NIJ7KE,57
|
|
7
|
-
django_structlog/celery/receivers.py,sha256=GBLrO5GKN3n9DlpbWptmIHx6WiTEfvXKFB7KDm6IzQ0,4859
|
|
8
|
-
django_structlog/celery/signals.py,sha256=7iksKd0Xaw2iUFWrPshn2p8nIjbRanrww6caeJsgYtQ,2116
|
|
9
|
-
django_structlog/celery/steps.py,sha256=i3vA9G3MgRzbhHELbx-yLZGjNAcJ7v2vpiCrFCFZ2vc,599
|
|
10
|
-
django_structlog/middlewares/__init__.py,sha256=yi0jJFSPaCt6ilsC4EMQaJEPFJn-uxtW3VmjPefTGvs,52
|
|
11
|
-
django_structlog/middlewares/request.py,sha256=-Cq3c8ENvIR4pge2kPAr6noBSsPNdIfX2-BrioEwUXk,7271
|
|
12
|
-
django_structlog-9.0.0.dev2.dist-info/LICENSE.rst,sha256=FmQJJGUr0CAup1Q81JpUuEGsjZM8PCBiNZUuoyHgtBQ,1079
|
|
13
|
-
django_structlog-9.0.0.dev2.dist-info/METADATA,sha256=wZD5Z2fp35_zdzhq8gMTtqK8Q2aoT0clkz-iXvZ30wk,27624
|
|
14
|
-
django_structlog-9.0.0.dev2.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
|
15
|
-
django_structlog-9.0.0.dev2.dist-info/top_level.txt,sha256=tGM_7H0Zgu3x7zaOXRkkZx-i18y6fJE9TJi5RhaYd0Y,17
|
|
16
|
-
django_structlog-9.0.0.dev2.dist-info/RECORD,,
|
|
File without changes
|
{django_structlog-9.0.0.dev2.dist-info → django_structlog-9.0.1.dev1.dist-info}/top_level.txt
RENAMED
|
File without changes
|