From 1a35dae8e4e961a64f5270a078b52e975f38a0b7 Mon Sep 17 00:00:00 2001 From: Neel Shah Date: Mon, 10 Jun 2024 20:51:07 +0200 Subject: [PATCH 1/2] Skeletons for new components --- .../opentelemetry/potel_span_exporter.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 sentry_sdk/integrations/opentelemetry/potel_span_exporter.py diff --git a/sentry_sdk/integrations/opentelemetry/potel_span_exporter.py b/sentry_sdk/integrations/opentelemetry/potel_span_exporter.py new file mode 100644 index 0000000000..70cfc39105 --- /dev/null +++ b/sentry_sdk/integrations/opentelemetry/potel_span_exporter.py @@ -0,0 +1,19 @@ +from opentelemetry.trace import Span # type: ignore + + +class PotelSentrySpanExporter: + """ + A Sentry-specific exporter that converts OpenTelemetry Spans to Sentry Spans & Transactions. + """ + + def __init__(self): + # type: () -> None + pass + + def export(self, span): + # type: (Span) -> None + pass + + def flush(self, timeout_millis): + # type: (int) -> bool + return True From c52318219ede830d14c797165e557df61fc78832 Mon Sep 17 00:00:00 2001 From: Neel Shah Date: Wed, 12 Jun 2024 13:36:37 +0200 Subject: [PATCH 2/2] Add simple scope management whenever a context is attached * create a new otel context `_SCOPES_KEY` that will hold a tuple of `(curent_scope, isolation_scope)` * the `current_scope` will always be forked (like on every span creation/context update in practice) * note that this is on `attach`, so not on all copy-on-write context object creation but only on apis such as [`trace.use_span`](https://github.com/open-telemetry/opentelemetry-python/blob/ba22b165471bde2037620f2c850ab648a849fbc0/opentelemetry-api/src/opentelemetry/trace/__init__.py#L547) or [`tracer.start_as_current_span`](https://github.com/open-telemetry/opentelemetry-python/blob/ba22b165471bde2037620f2c850ab648a849fbc0/opentelemetry-api/src/opentelemetry/trace/__init__.py#L329) * basically every otel `context` fork corresponds to our `current_scope` fork * the `isolation_scope` currently will not be forked * these will later be updated, for instance when we update our top level scope apis that fork isolation scope, that will also have a corresponding change in this `attach` function --- .../opentelemetry/contextvars_context.py | 26 ++++++++++++++----- .../integrations/opentelemetry/integration.py | 1 - .../opentelemetry/potel_span_exporter.py | 19 -------------- 3 files changed, 19 insertions(+), 27 deletions(-) delete mode 100644 sentry_sdk/integrations/opentelemetry/potel_span_exporter.py diff --git a/sentry_sdk/integrations/opentelemetry/contextvars_context.py b/sentry_sdk/integrations/opentelemetry/contextvars_context.py index 7a382064c9..3291fca448 100644 --- a/sentry_sdk/integrations/opentelemetry/contextvars_context.py +++ b/sentry_sdk/integrations/opentelemetry/contextvars_context.py @@ -1,14 +1,26 @@ -from opentelemetry.context.context import Context # type: ignore +from opentelemetry.context import Context, create_key, get_value, set_value from opentelemetry.context.contextvars_context import ContextVarsRuntimeContext # type: ignore +from sentry_sdk.scope import Scope + + +_SCOPES_KEY = create_key("sentry_scopes") + class SentryContextVarsRuntimeContext(ContextVarsRuntimeContext): # type: ignore def attach(self, context): # type: (Context) -> object - # TODO-neel-potel do scope management - return super().attach(context) + scopes = get_value(_SCOPES_KEY, context) + + if scopes and isinstance(scopes, tuple): + (current_scope, isolation_scope) = scopes + else: + current_scope = Scope.get_current_scope() + isolation_scope = Scope.get_isolation_scope() + + # TODO-neel-potel fork isolation_scope too like JS + # once we setup our own apis to pass through to otel + new_scopes = (current_scope.fork(), isolation_scope) + new_context = set_value(_SCOPES_KEY, new_scopes, context) - def detach(self, token): - # type: (object) -> None - # TODO-neel-potel not sure if we need anything here, see later - super().detach(token) + return super().attach(new_context) diff --git a/sentry_sdk/integrations/opentelemetry/integration.py b/sentry_sdk/integrations/opentelemetry/integration.py index 3a33c7f2d0..28a497c340 100644 --- a/sentry_sdk/integrations/opentelemetry/integration.py +++ b/sentry_sdk/integrations/opentelemetry/integration.py @@ -172,7 +172,6 @@ def _import_by_path(path): def _setup_sentry_tracing(): # type: () -> None - # TODO-neel-potel make sure lifecycle is correct # TODO-neel-potel contribute upstream so this is not necessary context._RUNTIME_CONTEXT = SentryContextVarsRuntimeContext() diff --git a/sentry_sdk/integrations/opentelemetry/potel_span_exporter.py b/sentry_sdk/integrations/opentelemetry/potel_span_exporter.py deleted file mode 100644 index 70cfc39105..0000000000 --- a/sentry_sdk/integrations/opentelemetry/potel_span_exporter.py +++ /dev/null @@ -1,19 +0,0 @@ -from opentelemetry.trace import Span # type: ignore - - -class PotelSentrySpanExporter: - """ - A Sentry-specific exporter that converts OpenTelemetry Spans to Sentry Spans & Transactions. - """ - - def __init__(self): - # type: () -> None - pass - - def export(self, span): - # type: (Span) -> None - pass - - def flush(self, timeout_millis): - # type: (int) -> bool - return True