|
6 | 6 |
|
7 | 7 | import asyncio
|
8 | 8 | import inspect
|
9 |
| -import urllib |
10 | 9 | from copy import deepcopy
|
11 | 10 |
|
12 | 11 | from sentry_sdk._functools import partial
|
13 | 12 | from sentry_sdk._types import TYPE_CHECKING
|
14 | 13 | from sentry_sdk.api import continue_trace
|
15 | 14 | from sentry_sdk.consts import OP
|
16 |
| -from sentry_sdk.hub import Hub, _should_send_default_pii |
17 |
| -from sentry_sdk.integrations._wsgi_common import _filter_headers |
| 15 | +from sentry_sdk.hub import Hub |
| 16 | + |
| 17 | +from sentry_sdk.integrations._asgi_common import ( |
| 18 | + _get_headers, |
| 19 | + _get_request_data, |
| 20 | +) |
18 | 21 | from sentry_sdk.integrations.modules import _get_installed_modules
|
19 | 22 | from sentry_sdk.sessions import auto_session_tracking
|
20 | 23 | from sentry_sdk.tracing import (
|
|
37 | 40 | from typing import Optional
|
38 | 41 | from typing import Callable
|
39 | 42 |
|
40 |
| - from typing_extensions import Literal |
41 |
| - |
42 | 43 | from sentry_sdk._types import Event, Hint
|
43 | 44 |
|
44 | 45 |
|
@@ -169,19 +170,32 @@ async def _run_app(self, scope, receive, send, asgi_version):
|
169 | 170 |
|
170 | 171 | if ty in ("http", "websocket"):
|
171 | 172 | transaction = continue_trace(
|
172 |
| - self._get_headers(scope), |
| 173 | + _get_headers(scope), |
173 | 174 | op="{}.server".format(ty),
|
174 | 175 | )
|
| 176 | + logger.debug( |
| 177 | + "[ASGI] Created transaction (continuing trace): %s", |
| 178 | + transaction, |
| 179 | + ) |
175 | 180 | else:
|
176 | 181 | transaction = Transaction(op=OP.HTTP_SERVER)
|
| 182 | + logger.debug( |
| 183 | + "[ASGI] Created transaction (new): %s", transaction |
| 184 | + ) |
177 | 185 |
|
178 | 186 | transaction.name = _DEFAULT_TRANSACTION_NAME
|
179 | 187 | transaction.source = TRANSACTION_SOURCE_ROUTE
|
180 | 188 | transaction.set_tag("asgi.type", ty)
|
| 189 | + logger.debug( |
| 190 | + "[ASGI] Set transaction name and source on transaction: '%s' / '%s'", |
| 191 | + transaction.name, |
| 192 | + transaction.source, |
| 193 | + ) |
181 | 194 |
|
182 | 195 | with hub.start_transaction(
|
183 | 196 | transaction, custom_sampling_context={"asgi_scope": scope}
|
184 | 197 | ):
|
| 198 | + logger.debug("[ASGI] Started transaction: %s", transaction) |
185 | 199 | try:
|
186 | 200 |
|
187 | 201 | async def _sentry_wrapped_send(event):
|
@@ -214,31 +228,15 @@ async def _sentry_wrapped_send(event):
|
214 | 228 |
|
215 | 229 | def event_processor(self, event, hint, asgi_scope):
|
216 | 230 | # type: (Event, Hint, Any) -> Optional[Event]
|
217 |
| - request_info = event.get("request", {}) |
218 |
| - |
219 |
| - ty = asgi_scope["type"] |
220 |
| - if ty in ("http", "websocket"): |
221 |
| - request_info["method"] = asgi_scope.get("method") |
222 |
| - request_info["headers"] = headers = _filter_headers( |
223 |
| - self._get_headers(asgi_scope) |
224 |
| - ) |
225 |
| - request_info["query_string"] = self._get_query(asgi_scope) |
226 |
| - |
227 |
| - request_info["url"] = self._get_url( |
228 |
| - asgi_scope, "http" if ty == "http" else "ws", headers.get("host") |
229 |
| - ) |
230 |
| - |
231 |
| - client = asgi_scope.get("client") |
232 |
| - if client and _should_send_default_pii(): |
233 |
| - request_info["env"] = {"REMOTE_ADDR": self._get_ip(asgi_scope)} |
| 231 | + request_data = event.get("request", {}) |
| 232 | + request_data.update(_get_request_data(asgi_scope)) |
| 233 | + event["request"] = deepcopy(request_data) |
234 | 234 |
|
235 | 235 | self._set_transaction_name_and_source(event, self.transaction_style, asgi_scope)
|
236 | 236 |
|
237 |
| - event["request"] = deepcopy(request_info) |
238 |
| - |
239 | 237 | return event
|
240 | 238 |
|
241 |
| - # Helper functions for extracting request data. |
| 239 | + # Helper functions. |
242 | 240 | #
|
243 | 241 | # Note: Those functions are not public API. If you want to mutate request
|
244 | 242 | # data to your liking it's recommended to use the `before_send` callback
|
@@ -275,71 +273,17 @@ def _set_transaction_name_and_source(self, event, transaction_style, asgi_scope)
|
275 | 273 | if not name:
|
276 | 274 | event["transaction"] = _DEFAULT_TRANSACTION_NAME
|
277 | 275 | event["transaction_info"] = {"source": TRANSACTION_SOURCE_ROUTE}
|
| 276 | + logger.debug( |
| 277 | + "[ASGI] Set default transaction name and source on event: '%s' / '%s'", |
| 278 | + event["transaction"], |
| 279 | + event["transaction_info"]["source"], |
| 280 | + ) |
278 | 281 | return
|
279 | 282 |
|
280 | 283 | event["transaction"] = name
|
281 | 284 | event["transaction_info"] = {"source": SOURCE_FOR_STYLE[transaction_style]}
|
282 |
| - |
283 |
| - def _get_url(self, scope, default_scheme, host): |
284 |
| - # type: (Dict[str, Any], Literal["ws", "http"], Optional[str]) -> str |
285 |
| - """ |
286 |
| - Extract URL from the ASGI scope, without also including the querystring. |
287 |
| - """ |
288 |
| - scheme = scope.get("scheme", default_scheme) |
289 |
| - |
290 |
| - server = scope.get("server", None) |
291 |
| - path = scope.get("root_path", "") + scope.get("path", "") |
292 |
| - |
293 |
| - if host: |
294 |
| - return "%s://%s%s" % (scheme, host, path) |
295 |
| - |
296 |
| - if server is not None: |
297 |
| - host, port = server |
298 |
| - default_port = {"http": 80, "https": 443, "ws": 80, "wss": 443}[scheme] |
299 |
| - if port != default_port: |
300 |
| - return "%s://%s:%s%s" % (scheme, host, port, path) |
301 |
| - return "%s://%s%s" % (scheme, host, path) |
302 |
| - return path |
303 |
| - |
304 |
| - def _get_query(self, scope): |
305 |
| - # type: (Any) -> Any |
306 |
| - """ |
307 |
| - Extract querystring from the ASGI scope, in the format that the Sentry protocol expects. |
308 |
| - """ |
309 |
| - qs = scope.get("query_string") |
310 |
| - if not qs: |
311 |
| - return None |
312 |
| - return urllib.parse.unquote(qs.decode("latin-1")) |
313 |
| - |
314 |
| - def _get_ip(self, scope): |
315 |
| - # type: (Any) -> str |
316 |
| - """ |
317 |
| - Extract IP Address from the ASGI scope based on request headers with fallback to scope client. |
318 |
| - """ |
319 |
| - headers = self._get_headers(scope) |
320 |
| - try: |
321 |
| - return headers["x-forwarded-for"].split(",")[0].strip() |
322 |
| - except (KeyError, IndexError): |
323 |
| - pass |
324 |
| - |
325 |
| - try: |
326 |
| - return headers["x-real-ip"] |
327 |
| - except KeyError: |
328 |
| - pass |
329 |
| - |
330 |
| - return scope.get("client")[0] |
331 |
| - |
332 |
| - def _get_headers(self, scope): |
333 |
| - # type: (Any) -> Dict[str, str] |
334 |
| - """ |
335 |
| - Extract headers from the ASGI scope, in the format that the Sentry protocol expects. |
336 |
| - """ |
337 |
| - headers = {} # type: Dict[str, str] |
338 |
| - for raw_key, raw_value in scope["headers"]: |
339 |
| - key = raw_key.decode("latin-1") |
340 |
| - value = raw_value.decode("latin-1") |
341 |
| - if key in headers: |
342 |
| - headers[key] = headers[key] + ", " + value |
343 |
| - else: |
344 |
| - headers[key] = value |
345 |
| - return headers |
| 285 | + logger.debug( |
| 286 | + "[ASGI] Set transaction name and source on event: '%s' / '%s'", |
| 287 | + event["transaction"], |
| 288 | + event["transaction_info"]["source"], |
| 289 | + ) |
0 commit comments