-
Notifications
You must be signed in to change notification settings - Fork 591
feat: Add enrichers for attribute-based telemetry #5725
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -12,6 +12,7 @@ | |||||||||||||||||||||||||||||
| from typing import Union | ||||||||||||||||||||||||||||||
| from typing_extensions import Literal | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| from sentry_sdk._types import Attributes | ||||||||||||||||||||||||||||||
| from sentry_sdk.utils import AnnotatedValue | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
|
@@ -105,3 +106,32 @@ def _get_request_data(asgi_scope: "Any") -> "Dict[str, Any]": | |||||||||||||||||||||||||||||
| request_data["env"] = {"REMOTE_ADDR": _get_ip(asgi_scope)} | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| return request_data | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| def _get_request_attributes(asgi_scope: "Any") -> "Dict[str, Any]": | ||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||
| Return attributes related to the HTTP request from the ASGI scope. | ||||||||||||||||||||||||||||||
| """ | ||||||||||||||||||||||||||||||
| attributes: "Attributes" = {} | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| ty = asgi_scope["type"] | ||||||||||||||||||||||||||||||
| if ty in ("http", "websocket"): | ||||||||||||||||||||||||||||||
| attributes["http.request.method"] = asgi_scope.get("method") | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| headers = _filter_headers(_get_headers(asgi_scope)) | ||||||||||||||||||||||||||||||
| # TODO[span-first]: Correctly merge headers if duplicate | ||||||||||||||||||||||||||||||
| for header, value in headers.items(): | ||||||||||||||||||||||||||||||
| attributes[f"http.request.headers.{header.lower()}"] = [value] | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| attributes["http.query"] = _get_query(asgi_scope) | ||||||||||||||||||||||||||||||
|
Comment on lines
+119
to
+126
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Null values assigned to attributes may cause downstream issues Both VerificationRead _types.py lines 226-240 confirming AttributeValue excludes None. Read _get_query function (lines 60-67) confirming it returns None when query string is falsy. Verified asgi_scope.get('method') can also return None. Suggested fix: Only set attributes when values are not None
Suggested change
Identified by Warden |
||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| attributes["url.full"] = _get_url( | ||||||||||||||||||||||||||||||
| asgi_scope, "http" if ty == "http" else "ws", headers.get("host") | ||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| # TODO[span-first]: Figure out where to put REMOTE_ADDR | ||||||||||||||||||||||||||||||
| # client = asgi_scope.get("client") | ||||||||||||||||||||||||||||||
| # if client and should_send_default_pii(): | ||||||||||||||||||||||||||||||
| # request_data["env"] = {"REMOTE_ADDR": _get_ip(asgi_scope)} | ||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||
| return attributes | ||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,6 +15,7 @@ | |
| from sentry_sdk.consts import OP | ||
| from sentry_sdk.integrations._asgi_common import ( | ||
| _get_headers, | ||
| _get_request_attributes, | ||
| _get_request_data, | ||
| _get_url, | ||
| ) | ||
|
|
@@ -23,7 +24,7 @@ | |
| nullcontext, | ||
| ) | ||
| from sentry_sdk.sessions import track_session | ||
| from sentry_sdk.traces import StreamedSpan | ||
| from sentry_sdk.traces import SegmentSource, StreamedSpan | ||
| from sentry_sdk.tracing import ( | ||
| SOURCE_FOR_STYLE, | ||
| Transaction, | ||
|
|
@@ -52,7 +53,7 @@ | |
| from typing import Tuple | ||
| from typing import Union | ||
|
|
||
| from sentry_sdk._types import Attributes, Event, Hint | ||
| from sentry_sdk._types import Attributes, Event, Hint, Telemetry | ||
| from sentry_sdk.tracing import Span | ||
|
|
||
|
|
||
|
|
@@ -214,7 +215,9 @@ | |
| sentry_scope.clear_breadcrumbs() | ||
| sentry_scope._name = "asgi" | ||
| processor = partial(self.event_processor, asgi_scope=scope) | ||
| enricher = partial(self.enricher, asgi_scope=scope) | ||
| sentry_scope.add_event_processor(processor) | ||
| sentry_scope._add_enricher(enricher) | ||
|
|
||
| ty = scope["type"] | ||
| ( | ||
|
|
@@ -382,6 +385,32 @@ | |
|
|
||
| return event | ||
|
|
||
| def enricher(self, telemetry: "Telemetry", asgi_scope: "Any") -> "Telemetry": | ||
sentry-warden[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| request_attributes = _get_request_attributes(asgi_scope) | ||
sentry-warden[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| telemetry.set_attributes(request_attributes) | ||
|
|
||
| # Only set transaction name if not already set by Starlette or FastAPI (or other frameworks) | ||
| segment = telemetry.segment | ||
| source = segment.get_attributes.get("sentry.span.source") | ||
|
Check failure on line 394 in sentry_sdk/integrations/asgi.py
|
||
| already_set = ( | ||
| segment is not None | ||
| and segment.name != _DEFAULT_TRANSACTION_NAME | ||
| and source | ||
| in [ | ||
| SegmentSource.COMPONENT, | ||
| SegmentSource.ROUTE, | ||
| SegmentSource.CUSTOM, | ||
| ] | ||
| ) | ||
|
Check failure on line 404 in sentry_sdk/integrations/asgi.py
|
||
|
Comment on lines
+393
to
+404
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Null check for segment occurs after segment is accessed, causing potential AttributeError Line 394 accesses VerificationRead lines 393-404 in the hunk: line 393 assigns Identified by Warden |
||
| if not already_set: | ||
| name, source = self._get_transaction_name_and_source( | ||
| self.transaction_style, asgi_scope | ||
| ) | ||
| segment.name = name | ||
| segment.set_attribute("sentry.span.source", source) | ||
|
|
||
| return telemetry | ||
|
|
||
| # Helper functions. | ||
| # | ||
| # Note: Those functions are not public API. If you want to mutate request | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.