1
1
import json
2
2
3
3
import sentry_sdk
4
- from sentry_sdk .integrations import Integration
4
+ from sentry_sdk .consts import OP , SPANSTATUS
5
+ from sentry_sdk .api import continue_trace , get_baggage , get_traceparent
6
+ from sentry_sdk .integrations import Integration , DidNotEnable
5
7
from sentry_sdk .integrations ._wsgi_common import request_body_within_bounds
8
+ from sentry_sdk .tracing import (
9
+ BAGGAGE_HEADER_NAME ,
10
+ SENTRY_TRACE_HEADER_NAME ,
11
+ TransactionSource ,
12
+ )
6
13
from sentry_sdk .utils import (
7
14
AnnotatedValue ,
8
15
capture_internal_exceptions ,
9
16
event_from_exception ,
10
17
)
18
+ from typing import TypeVar
19
+
20
+ R = TypeVar ("R" )
11
21
12
- from dramatiq .broker import Broker # type: ignore
13
- from dramatiq .message import Message # type: ignore
14
- from dramatiq .middleware import Middleware , default_middleware # type: ignore
15
- from dramatiq .errors import Retry # type: ignore
22
+ try :
23
+ from dramatiq .broker import Broker
24
+ from dramatiq .middleware import Middleware , default_middleware
25
+ from dramatiq .errors import Retry
26
+ from dramatiq .message import Message
27
+ except ImportError :
28
+ raise DidNotEnable ("Dramatiq is not installed" )
16
29
17
30
from typing import TYPE_CHECKING
18
31
@@ -34,10 +47,12 @@ class DramatiqIntegration(Integration):
34
47
"""
35
48
36
49
identifier = "dramatiq"
50
+ origin = f"auto.queue.{ identifier } "
37
51
38
52
@staticmethod
39
53
def setup_once ():
40
54
# type: () -> None
55
+
41
56
_patch_dramatiq_broker ()
42
57
43
58
@@ -85,50 +100,93 @@ class SentryMiddleware(Middleware): # type: ignore[misc]
85
100
DramatiqIntegration.
86
101
"""
87
102
88
- def before_process_message (self , broker , message ):
89
- # type: (Broker, Message) -> None
103
+ SENTRY_HEADERS_NAME = "_sentry_headers"
104
+
105
+ def before_enqueue (self , broker , message , delay ):
106
+ # type: (Broker, Message[R], int) -> None
90
107
integration = sentry_sdk .get_client ().get_integration (DramatiqIntegration )
91
108
if integration is None :
92
109
return
93
110
94
- message ._scope_manager = sentry_sdk .new_scope ()
95
- message ._scope_manager .__enter__ ()
111
+ message .options [self .SENTRY_HEADERS_NAME ] = {
112
+ BAGGAGE_HEADER_NAME : get_baggage (),
113
+ SENTRY_TRACE_HEADER_NAME : get_traceparent (),
114
+ }
115
+
116
+ def before_process_message (self , broker , message ):
117
+ # type: (Broker, Message[R]) -> None
118
+ integration = sentry_sdk .get_client ().get_integration (DramatiqIntegration )
119
+ if integration is None :
120
+ return
96
121
97
- scope = sentry_sdk .get_current_scope ()
98
- scope .set_transaction_name (message .actor_name )
122
+ message ._scope_manager = sentry_sdk .isolation_scope ()
123
+ scope = message ._scope_manager .__enter__ ()
124
+ scope .clear_breadcrumbs ()
99
125
scope .set_extra ("dramatiq_message_id" , message .message_id )
100
126
scope .add_event_processor (_make_message_event_processor (message , integration ))
101
127
128
+ sentry_headers = message .options .get (self .SENTRY_HEADERS_NAME ) or {}
129
+ if "retries" in message .options :
130
+ # start new trace in case of retrying
131
+ sentry_headers = {}
132
+
133
+ transaction = continue_trace (
134
+ sentry_headers ,
135
+ name = message .actor_name ,
136
+ op = OP .QUEUE_TASK_DRAMATIQ ,
137
+ source = TransactionSource .TASK ,
138
+ origin = DramatiqIntegration .origin ,
139
+ )
140
+ transaction .set_status (SPANSTATUS .OK )
141
+ sentry_sdk .start_transaction (
142
+ transaction ,
143
+ name = message .actor_name ,
144
+ op = OP .QUEUE_TASK_DRAMATIQ ,
145
+ source = TransactionSource .TASK ,
146
+ )
147
+ transaction .__enter__ ()
148
+
102
149
def after_process_message (self , broker , message , * , result = None , exception = None ):
103
- # type: (Broker, Message, Any , Optional[Any], Optional[Exception]) -> None
150
+ # type: (Broker, Message[R] , Optional[Any], Optional[Exception]) -> None
104
151
integration = sentry_sdk .get_client ().get_integration (DramatiqIntegration )
105
152
if integration is None :
106
153
return
107
154
108
155
actor = broker .get_actor (message .actor_name )
109
156
throws = message .options .get ("throws" ) or actor .options .get ("throws" )
110
157
111
- try :
112
- if (
113
- exception is not None
114
- and not (throws and isinstance (exception , throws ))
115
- and not isinstance (exception , Retry )
116
- ):
117
- event , hint = event_from_exception (
118
- exception ,
119
- client_options = sentry_sdk .get_client ().options ,
120
- mechanism = {
121
- "type" : DramatiqIntegration .identifier ,
122
- "handled" : False ,
123
- },
124
- )
125
- sentry_sdk .capture_event (event , hint = hint )
126
- finally :
127
- message ._scope_manager .__exit__ (None , None , None )
158
+ scope_manager = message ._scope_manager
159
+ transaction = sentry_sdk .get_current_scope ().transaction
160
+ if not transaction :
161
+ return None
162
+
163
+ is_event_capture_required = (
164
+ exception is not None
165
+ and not (throws and isinstance (exception , throws ))
166
+ and not isinstance (exception , Retry )
167
+ )
168
+ if not is_event_capture_required :
169
+ # normal transaction finish
170
+ transaction .__exit__ (None , None , None )
171
+ scope_manager .__exit__ (None , None , None )
172
+ return
173
+
174
+ event , hint = event_from_exception (
175
+ exception , # type: ignore[arg-type]
176
+ client_options = sentry_sdk .get_client ().options ,
177
+ mechanism = {
178
+ "type" : DramatiqIntegration .identifier ,
179
+ "handled" : False ,
180
+ },
181
+ )
182
+ sentry_sdk .capture_event (event , hint = hint )
183
+ # transaction error
184
+ transaction .__exit__ (type (exception ), exception , None )
185
+ scope_manager .__exit__ (type (exception ), exception , None )
128
186
129
187
130
188
def _make_message_event_processor (message , integration ):
131
- # type: (Message, DramatiqIntegration) -> Callable[[Event, Hint], Optional[Event]]
189
+ # type: (Message[R] , DramatiqIntegration) -> Callable[[Event, Hint], Optional[Event]]
132
190
133
191
def inner (event , hint ):
134
192
# type: (Event, Hint) -> Optional[Event]
@@ -142,7 +200,7 @@ def inner(event, hint):
142
200
143
201
class DramatiqMessageExtractor :
144
202
def __init__ (self , message ):
145
- # type: (Message) -> None
203
+ # type: (Message[R] ) -> None
146
204
self .message_data = dict (message .asdict ())
147
205
148
206
def content_length (self ):
0 commit comments