Skip to content

Commit

Permalink
Use root span transaction name in populated head DSC
Browse files Browse the repository at this point in the history
  • Loading branch information
sl0thentr0py committed Oct 21, 2024
1 parent 8e339bd commit ba53b3d
Show file tree
Hide file tree
Showing 4 changed files with 42 additions and 46 deletions.
23 changes: 5 additions & 18 deletions sentry_sdk/integrations/opentelemetry/integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@
try:
from opentelemetry import trace
from opentelemetry.propagate import set_global_textmap
from opentelemetry.trace import Span as AbstractSpan
from opentelemetry.sdk.trace import TracerProvider, Span, ReadableSpan
except ImportError:
raise DidNotEnable("opentelemetry not installed")
Expand All @@ -28,11 +27,6 @@
except ImportError:
DjangoInstrumentor = None

from sentry_sdk._types import TYPE_CHECKING

if TYPE_CHECKING:
from typing import Union, Any


CONFIGURABLE_INSTRUMENTATIONS = {
DjangoInstrumentor: {"is_sql_commentor_enabled": True},
Expand Down Expand Up @@ -63,23 +57,16 @@ def _patch_readable_span():
We need to pass through sentry specific metadata/objects from Span to ReadableSpan
to work with them consistently in the SpanProcessor.
"""

@property
def sentry_meta(self):
# type: (Union[AbstractSpan, Span, ReadableSpan]) -> dict[str, Any]
if not getattr(self, "_sentry_meta", None):
self._sentry_meta = {}
return self._sentry_meta

AbstractSpan.sentry_meta = sentry_meta
ReadableSpan.sentry_meta = sentry_meta

old_readable_span = Span._readable_span

def sentry_patched_readable_span(self):
# type: (Span) -> ReadableSpan
readable_span = old_readable_span(self)
readable_span._sentry_meta = self._sentry_meta

sentry_meta = getattr(self, "_sentry_meta", None)
if sentry_meta:
setattr(readable_span, "_sentry_meta", sentry_meta)

return readable_span

Span._readable_span = sentry_patched_readable_span
Expand Down
9 changes: 5 additions & 4 deletions sentry_sdk/integrations/opentelemetry/potel_span_processor.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
extract_span_data,
extract_transaction_name_source,
get_trace_context,
get_sentry_meta,
set_sentry_meta,
)
from sentry_sdk.integrations.opentelemetry.consts import (
OTEL_SENTRY_CONTEXT,
Expand Down Expand Up @@ -85,12 +87,11 @@ def _add_root_span(self, span, parent_span):
"""
if parent_span != INVALID_SPAN and not parent_span.get_span_context().is_remote:
# child span points to parent's root or parent
span.sentry_meta["root_span"] = parent_span.sentry_meta.get(
"root_span", parent_span
)
parent_root_span = get_sentry_meta(parent_span, "root_span")
set_sentry_meta(span, "root_span", parent_root_span or parent_span)
else:
# root span points to itself
span.sentry_meta["root_span"] = span
set_sentry_meta(span, "root_span", span)

def _flush_root_span(self, span):
# type: (ReadableSpan) -> None
Expand Down
50 changes: 27 additions & 23 deletions sentry_sdk/integrations/opentelemetry/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from urllib3.util import parse_url as urlparse
from urllib.parse import quote
from opentelemetry.trace import (
Span,
Span as AbstractSpan,
SpanKind,
StatusCode,
format_trace_id,
Expand Down Expand Up @@ -365,7 +365,7 @@ def has_incoming_trace(trace_state):


def get_trace_state(span):
# type: (Union[Span, ReadableSpan]) -> TraceState
# type: (Union[AbstractSpan, ReadableSpan]) -> TraceState
"""
Get the existing trace_state with sentry items
or populate it if we are the head SDK.
Expand Down Expand Up @@ -404,27 +404,31 @@ def get_trace_state(span):
Baggage.SENTRY_PREFIX + "public_key", Dsn(options["dsn"]).public_key
)

# 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
root_span = get_sentry_meta(span, "root_span")
if root_span and isinstance(root_span, ReadableSpan):
transaction_name, transaction_source = extract_transaction_name_source(
root_span
)

if (
transaction_name
and transaction_source not in LOW_QUALITY_TRANSACTION_SOURCES
):
trace_state = trace_state.update(
Baggage.SENTRY_PREFIX + "transaction", transaction_name
)

return trace_state


def get_sentry_meta(span, key):
# type: (Union[AbstractSpan, ReadableSpan], str) -> Any
sentry_meta = getattr(span, "_sentry_meta", None)
return sentry_meta.get(key) if sentry_meta else None


def set_sentry_meta(span, key, value):
# type: (Union[AbstractSpan, ReadableSpan], str, Any) -> None
sentry_meta = getattr(span, "_sentry_meta", {})
sentry_meta[key] = value
setattr(span, "_sentry_meta", sentry_meta)
6 changes: 5 additions & 1 deletion sentry_sdk/tracing.py
Original file line number Diff line number Diff line change
Expand Up @@ -1326,8 +1326,12 @@ def containing_transaction(self):
@property
def root_span(self):
# type: () -> Optional[POTelSpan]
from sentry_sdk.integrations.opentelemetry.utils import (
get_sentry_meta,
)

root_otel_span = cast(
"Optional[OtelSpan]", self._otel_span.sentry_meta.get("root_span", None)
"Optional[OtelSpan]", get_sentry_meta(self._otel_span, "root_span")
)
return POTelSpan(otel_span=root_otel_span) if root_otel_span else None

Expand Down

0 comments on commit ba53b3d

Please sign in to comment.