Skip to content

Make transaction names work #3643

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

Merged
merged 1 commit into from
Oct 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions sentry_sdk/integrations/opentelemetry/consts.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,4 +28,5 @@ class SentrySpanAttribute:
MEASUREMENT = "sentry.measurement"
TAG = "sentry.tag"
NAME = "sentry.name"
SOURCE = "sentry.source"
CONTEXT = "sentry.context"
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
convert_from_otel_timestamp,
extract_span_attributes,
extract_span_data,
extract_transaction_name_source,
get_trace_context,
)
from sentry_sdk.integrations.opentelemetry.consts import (
Expand Down Expand Up @@ -137,6 +138,7 @@ def _root_span_to_transaction_event(self, span):
if event is None:
return None

transaction_name, transaction_source = extract_transaction_name_source(span)
span_data = extract_span_data(span)
(_, description, _, http_status, _) = span_data

Expand All @@ -152,9 +154,8 @@ def _root_span_to_transaction_event(self, span):
event.update(
{
"type": "transaction",
"transaction": description,
# TODO-neel-potel tx source based on integration
"transaction_info": {"source": "custom"},
"transaction": transaction_name or description,
"transaction_info": {"source": transaction_source or "custom"},
"contexts": contexts,
}
)
Expand Down
36 changes: 34 additions & 2 deletions sentry_sdk/integrations/opentelemetry/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
from sentry_sdk.utils import Dsn
from sentry_sdk.consts import SPANSTATUS, OP
from sentry_sdk.tracing import get_span_status_from_http_code, DEFAULT_SPAN_ORIGIN
from sentry_sdk.tracing_utils import Baggage
from sentry_sdk.tracing_utils import Baggage, LOW_QUALITY_TRANSACTION_SOURCES
from sentry_sdk.integrations.opentelemetry.consts import SentrySpanAttribute

from sentry_sdk._types import TYPE_CHECKING
Expand Down Expand Up @@ -98,6 +98,16 @@ def convert_to_otel_timestamp(time):
return int(time * 1e9)


def extract_transaction_name_source(span):
# type: (ReadableSpan) -> tuple[Optional[str], Optional[str]]
if not span.attributes:
return (None, None)
return (
cast("Optional[str]", span.attributes.get(SentrySpanAttribute.NAME)),
cast("Optional[str]", span.attributes.get(SentrySpanAttribute.SOURCE)),
)


def extract_span_data(span):
# type: (ReadableSpan) -> OtelExtractedSpanData
op = span.name
Expand Down Expand Up @@ -394,5 +404,27 @@ def get_trace_state(span):
Baggage.SENTRY_PREFIX + "public_key", Dsn(options["dsn"]).public_key
)

# TODO-neel-potel head dsc transaction name
# we cannot access the root span in most cases here, so we HAVE to rely on the
# scopes to carry the correct transaction name/source.
# IDEALLY we will always move to using the isolation scope here
# but our integrations do all kinds of stuff with both isolation and current
# so I am keeping both for now as a best attempt solution till we get to a better state.
isolation_scope = sentry_sdk.get_isolation_scope()
current_scope = sentry_sdk.get_current_scope()
if (
current_scope.transaction_name
and current_scope.transaction_source not in LOW_QUALITY_TRANSACTION_SOURCES
):
trace_state = trace_state.update(
Baggage.SENTRY_PREFIX + "transaction", current_scope.transaction_name
)
elif (
isolation_scope.transaction_name
and isolation_scope.transaction_source
not in LOW_QUALITY_TRANSACTION_SOURCES
):
trace_state = trace_state.update(
Baggage.SENTRY_PREFIX + "transaction", isolation_scope.transaction_name
)

return trace_state
10 changes: 10 additions & 0 deletions sentry_sdk/scope.py
Original file line number Diff line number Diff line change
Expand Up @@ -741,6 +741,16 @@ def set_transaction_name(self, name, source=None):
if source:
self._transaction_info["source"] = source

@property
def transaction_name(self):
# type: () -> Optional[str]
return self._transaction

@property
def transaction_source(self):
# type: () -> Optional[str]
return self._transaction_info.get("source")

@_attr_setter
def user(self, value):
# type: (Optional[Dict[str, Any]]) -> None
Expand Down
18 changes: 12 additions & 6 deletions sentry_sdk/tracing.py
Original file line number Diff line number Diff line change
Expand Up @@ -1292,7 +1292,7 @@ def description(self, value):
from sentry_sdk.integrations.opentelemetry.consts import SentrySpanAttribute

if value is not None:
self._otel_span.set_attribute(SentrySpanAttribute.DESCRIPTION, value)
self.set_attribute(SentrySpanAttribute.DESCRIPTION, value)

@property
def origin(self):
Expand All @@ -1307,7 +1307,7 @@ def origin(self, value):
from sentry_sdk.integrations.opentelemetry.consts import SentrySpanAttribute

if value is not None:
self._otel_span.set_attribute(SentrySpanAttribute.ORIGIN, value)
self.set_attribute(SentrySpanAttribute.ORIGIN, value)

@property
def containing_transaction(self):
Expand Down Expand Up @@ -1380,7 +1380,7 @@ def op(self, value):
from sentry_sdk.integrations.opentelemetry.consts import SentrySpanAttribute

if value is not None:
self._otel_span.set_attribute(SentrySpanAttribute.OP, value)
self.set_attribute(SentrySpanAttribute.OP, value)

@property
def name(self):
Expand All @@ -1395,17 +1395,23 @@ def name(self, value):
from sentry_sdk.integrations.opentelemetry.consts import SentrySpanAttribute

if value is not None:
self._otel_span.set_attribute(SentrySpanAttribute.NAME, value)
self.set_attribute(SentrySpanAttribute.NAME, value)

@property
def source(self):
# type: () -> str
pass
from sentry_sdk.integrations.opentelemetry.consts import SentrySpanAttribute

return (
self.get_attribute(SentrySpanAttribute.SOURCE) or TRANSACTION_SOURCE_CUSTOM
)

@source.setter
def source(self, value):
# type: (str) -> None
pass
from sentry_sdk.integrations.opentelemetry.consts import SentrySpanAttribute

self.set_attribute(SentrySpanAttribute.SOURCE, value)

@property
def start_timestamp(self):
Expand Down
2 changes: 2 additions & 0 deletions tests/integrations/flask/test_flask.py
Original file line number Diff line number Diff line change
Expand Up @@ -751,12 +751,14 @@ def hi_tx():

assert transaction_event["type"] == "transaction"
assert transaction_event["transaction"] == "hi_tx"
assert transaction_event["transaction_info"] == {"source": "component"}
assert transaction_event["contexts"]["trace"]["status"] == "ok"
assert transaction_event["tags"]["view"] == "yes"
assert transaction_event["tags"]["before_request"] == "yes"

assert message_event["message"] == "hi"
assert message_event["transaction"] == "hi_tx"
assert message_event["transaction_info"] == {"source": "component"}
assert message_event["tags"]["view"] == "yes"
assert message_event["tags"]["before_request"] == "yes"

Expand Down
Loading