From 259d4c5ae245d5a450e6359053e25bd23f96d3b1 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Thu, 11 Apr 2024 15:54:34 +0200 Subject: [PATCH 01/10] Having a class representing the propagation context --- sentry_sdk/scope.py | 81 ++++++++---------------- sentry_sdk/tracing_utils.py | 58 ++++++++++++++++- tests/integrations/celery/test_celery.py | 4 +- tests/integrations/rq/test_rq.py | 2 +- tests/test_api.py | 14 ++-- 5 files changed, 93 insertions(+), 66 deletions(-) diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 3bcf99579c..3acd163673 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -1,6 +1,5 @@ import os import sys -import uuid from copy import copy from collections import deque from contextlib import contextmanager @@ -18,6 +17,7 @@ extract_sentrytrace_data, has_tracing_enabled, normalize_incoming_data, + PropagationContext, ) from sentry_sdk.tracing import ( BAGGAGE_HEADER_NAME, @@ -196,7 +196,7 @@ def __init__(self, ty=None, client=None): self._error_processors = [] # type: List[ErrorProcessor] self._name = None # type: Optional[str] - self._propagation_context = None # type: Optional[Dict[str, Any]] + self._propagation_context = None # type: Optional[PropagationContext] self.client = NonRecordingClient() # type: sentry_sdk.client.BaseClient @@ -431,14 +431,15 @@ def _load_trace_data_from_env(self): return incoming_trace_information or None - def _extract_propagation_context(self, data): - # type: (Dict[str, Any]) -> Optional[Dict[str, Any]] - context = {} # type: Dict[str, Any] - normalized_data = normalize_incoming_data(data) + def _extract_propagation_context(self, incoming_data): + # type: (Dict[str, Any]) -> Optional[PropagationContext] + context = None + normalized_data = normalize_incoming_data(incoming_data) baggage_header = normalized_data.get(BAGGAGE_HEADER_NAME) if baggage_header: - context["dynamic_sampling_context"] = Baggage.from_incoming_header( + context = PropagationContext() + context.dynamic_sampling_context = Baggage.from_incoming_header( baggage_header ).dynamic_sampling_context() @@ -446,41 +447,18 @@ def _extract_propagation_context(self, data): if sentry_trace_header: sentrytrace_data = extract_sentrytrace_data(sentry_trace_header) if sentrytrace_data is not None: + if context is None: + context = PropagationContext() context.update(sentrytrace_data) - only_baggage_no_sentry_trace = ( - "dynamic_sampling_context" in context and "trace_id" not in context - ) - if only_baggage_no_sentry_trace: - context.update(self._create_new_propagation_context()) - - if context: - if not context.get("span_id"): - context["span_id"] = uuid.uuid4().hex[16:] - - return context - - return None - - def _create_new_propagation_context(self): - # type: () -> Dict[str, Any] - return { - "trace_id": uuid.uuid4().hex, - "span_id": uuid.uuid4().hex[16:], - "parent_span_id": None, - "dynamic_sampling_context": None, - } + return context def set_new_propagation_context(self): # type: () -> None """ Creates a new propagation context and sets it as `_propagation_context`. Overwriting existing one. """ - self._propagation_context = self._create_new_propagation_context() - logger.debug( - "[Tracing] Create new propagation context: %s", - self._propagation_context, - ) + self._propagation_context = PropagationContext() def generate_propagation_context(self, incoming_data=None): # type: (Optional[Dict[str, str]]) -> None @@ -495,10 +473,6 @@ def generate_propagation_context(self, incoming_data=None): if context is not None: self._propagation_context = context - logger.debug( - "[Tracing] Extracted propagation context from incoming data: %s", - self._propagation_context, - ) if self._propagation_context is None and self._type != ScopeType.CURRENT: self.set_new_propagation_context() @@ -514,11 +488,11 @@ def get_dynamic_sampling_context(self): baggage = self.get_baggage() if baggage is not None: - self._propagation_context["dynamic_sampling_context"] = ( + self._propagation_context.dynamic_sampling_context = ( baggage.dynamic_sampling_context() ) - return self._propagation_context["dynamic_sampling_context"] + return self._propagation_context.dynamic_sampling_context def get_traceparent(self, *args, **kwargs): # type: (Any, Any) -> Optional[str] @@ -535,8 +509,8 @@ def get_traceparent(self, *args, **kwargs): # If this scope has a propagation context, return traceparent from there if self._propagation_context is not None: traceparent = "%s-%s" % ( - self._propagation_context["trace_id"], - self._propagation_context["span_id"], + self._propagation_context.trace_id, + self._propagation_context.span_id, ) return traceparent @@ -557,8 +531,8 @@ def get_baggage(self, *args, **kwargs): # If this scope has a propagation context, return baggage from there if self._propagation_context is not None: - dynamic_sampling_context = self._propagation_context.get( - "dynamic_sampling_context" + dynamic_sampling_context = ( + self._propagation_context.dynamic_sampling_context ) if dynamic_sampling_context is None: return Baggage.from_options(self) @@ -577,9 +551,9 @@ def get_trace_context(self): return None trace_context = { - "trace_id": self._propagation_context["trace_id"], - "span_id": self._propagation_context["span_id"], - "parent_span_id": self._propagation_context["parent_span_id"], + "trace_id": self._propagation_context.trace_id, + "span_id": self._propagation_context.span_id, + "parent_span_id": self._propagation_context.parent_span_id, "dynamic_sampling_context": self.get_dynamic_sampling_context(), } # type: Dict[str, Any] @@ -667,7 +641,7 @@ def iter_trace_propagation_headers(self, *args, **kwargs): yield header def get_active_propagation_context(self): - # type: () -> Dict[str, Any] + # type: () -> Optional[PropagationContext] if self._propagation_context is not None: return self._propagation_context @@ -679,7 +653,7 @@ def get_active_propagation_context(self): if isolation_scope._propagation_context is not None: return isolation_scope._propagation_context - return {} + return None def clear(self): # type: () -> None @@ -1069,12 +1043,11 @@ def start_span(self, instrumenter=INSTRUMENTER.SENTRY, **kwargs): span = self.span or Scope.get_isolation_scope().span if span is None: - # New spans get the `trace_id`` from the scope + # New spans get the `trace_id` from the scope if "trace_id" not in kwargs: - - trace_id = self.get_active_propagation_context().get("trace_id") - if trace_id is not None: - kwargs["trace_id"] = trace_id + propagation_context = self.get_active_propagation_context() + if propagation_context is not None: + kwargs["trace_id"] = propagation_context.trace_id span = Span(**kwargs) else: diff --git a/sentry_sdk/tracing_utils.py b/sentry_sdk/tracing_utils.py index 06e6219233..bdb7d9e98f 100644 --- a/sentry_sdk/tracing_utils.py +++ b/sentry_sdk/tracing_utils.py @@ -7,6 +7,7 @@ from datetime import timedelta from functools import wraps from urllib.parse import quote, unquote +import uuid import sentry_sdk from sentry_sdk.consts import OP, SPANDATA @@ -318,6 +319,59 @@ def _format_sql(cursor, sql): return real_sql or to_string(sql) +class PropagationContext: + """ + The PropagationContext represents the data of a trace in Sentry. + """ + + __slots__ = ( + "_trace_id", + "_span_id", + "parent_span_id", + "parent_sampled", + "dynamic_sampling_context", + ) + + def __init__(self): + # type: () -> None + self._trace_id = None # type: Optional[str] + self._span_id = None # type: Optional[str] + self.parent_span_id = None # type: Optional[str] + self.parent_sampled = None # type: Optional[bool] + self.dynamic_sampling_context = None # type: Optional[Dict[str, str]] + + @property + def trace_id(self): + # type: () -> str + if not self._trace_id: + self._trace_id = uuid.uuid4().hex + + return self._trace_id + + @trace_id.setter + def trace_id(self, value): + # type: (str) -> None + self._trace_id = value + + @property + def span_id(self): + # type: () -> str + if not self._span_id: + self._span_id = uuid.uuid4().hex[16:] + + return self._span_id + + @span_id.setter + def span_id(self, value): + # type: (str) -> None + self._span_id = value + + def update(self, other_dict): + # type: (Dict[str, Any]) -> None + for key, value in other_dict.items(): + setattr(self, key, value) + + class Baggage: """ The W3C Baggage header information (see https://www.w3.org/TR/baggage/). @@ -381,8 +435,8 @@ def from_options(cls, scope): options = client.options propagation_context = scope._propagation_context - if propagation_context is not None and "trace_id" in propagation_context: - sentry_items["trace_id"] = propagation_context["trace_id"] + if propagation_context is not None: + sentry_items["trace_id"] = propagation_context.trace_id if options.get("environment"): sentry_items["environment"] = options["environment"] diff --git a/tests/integrations/celery/test_celery.py b/tests/integrations/celery/test_celery.py index 7e0b533d4c..63fcab3b53 100644 --- a/tests/integrations/celery/test_celery.py +++ b/tests/integrations/celery/test_celery.py @@ -154,11 +154,11 @@ def dummy_task(x, y): assert ( error_event["contexts"]["trace"]["trace_id"] - == scope._propagation_context["trace_id"] + == scope._propagation_context.trace_id ) assert ( error_event["contexts"]["trace"]["span_id"] - != scope._propagation_context["span_id"] + != scope._propagation_context.span_id ) assert error_event["transaction"] == "dummy_task" assert "celery_task_id" in error_event["tags"] diff --git a/tests/integrations/rq/test_rq.py b/tests/integrations/rq/test_rq.py index 3f79f531ff..094a458063 100644 --- a/tests/integrations/rq/test_rq.py +++ b/tests/integrations/rq/test_rq.py @@ -190,7 +190,7 @@ def test_tracing_disabled( assert error_event["transaction"] == "tests.integrations.rq.test_rq.crashing_job" assert ( error_event["contexts"]["trace"]["trace_id"] - == scope._propagation_context["trace_id"] + == scope._propagation_context.trace_id ) diff --git a/tests/test_api.py b/tests/test_api.py index d69c33cf93..738882f965 100644 --- a/tests/test_api.py +++ b/tests/test_api.py @@ -66,8 +66,8 @@ def test_traceparent_with_tracing_disabled(sentry_init): propagation_context = Scope.get_isolation_scope()._propagation_context expected_traceparent = "%s-%s" % ( - propagation_context["trace_id"], - propagation_context["span_id"], + propagation_context.trace_id, + propagation_context.span_id, ) assert get_traceparent() == expected_traceparent @@ -78,7 +78,7 @@ def test_baggage_with_tracing_disabled(sentry_init): propagation_context = Scope.get_isolation_scope()._propagation_context expected_baggage = ( "sentry-trace_id={},sentry-environment=dev,sentry-release=1.0.0".format( - propagation_context["trace_id"] + propagation_context.trace_id ) ) assert get_baggage() == expected_baggage @@ -112,10 +112,10 @@ def test_continue_trace(sentry_init): assert transaction.name == "some name" propagation_context = Scope.get_isolation_scope()._propagation_context - assert propagation_context["trace_id"] == transaction.trace_id == trace_id - assert propagation_context["parent_span_id"] == parent_span_id - assert propagation_context["parent_sampled"] == parent_sampled - assert propagation_context["dynamic_sampling_context"] == { + assert propagation_context.trace_id == transaction.trace_id == trace_id + assert propagation_context.parent_span_id == parent_span_id + assert propagation_context.parent_sampled == parent_sampled + assert propagation_context.dynamic_sampling_context == { "trace_id": "566e3688a61d4bc888951642d6f14a19" } From be95034bfa5c32fdb575d1fe1cdf60e8dc5a5c7f Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Thu, 11 Apr 2024 16:38:49 +0200 Subject: [PATCH 02/10] Apidocs --- sentry_sdk/tracing_utils.py | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/sentry_sdk/tracing_utils.py b/sentry_sdk/tracing_utils.py index bdb7d9e98f..5be27d5c3e 100644 --- a/sentry_sdk/tracing_utils.py +++ b/sentry_sdk/tracing_utils.py @@ -332,17 +332,34 @@ class PropagationContext: "dynamic_sampling_context", ) - def __init__(self): - # type: () -> None - self._trace_id = None # type: Optional[str] - self._span_id = None # type: Optional[str] - self.parent_span_id = None # type: Optional[str] - self.parent_sampled = None # type: Optional[bool] - self.dynamic_sampling_context = None # type: Optional[Dict[str, str]] + def __init__( + self, + trace_id=None, # type: Optional[str] + span_id=None, # type: Optional[str] + parent_span_id=None, # type: Optional[str] + parent_sampled=None, # type: Optional[bool] + dynamic_sampling_context=None, # type: Optional[Dict[str, str]] + ): + # type: (...) -> None + self._trace_id = trace_id + """The trace id of the Sentry trace.""" + + self._span_id = span_id + """The span id of the currently executing span.""" + + self.parent_span_id = parent_span_id + """The id of the parent span that started this span. The parent span could also be a span in an upstream service.""" + + self.parent_sampled = parent_sampled + """Boolean indicator if the parent span was sampled. Important when the parent span originated in an upstream service, because we watn to sample the whole trace, or nothing from the trace.""" + + self.dynamic_sampling_context = dynamic_sampling_context + """Data that is used for dynamic sampling decisions.""" @property def trace_id(self): # type: () -> str + """The trace id of the Sentry trace.""" if not self._trace_id: self._trace_id = uuid.uuid4().hex @@ -356,6 +373,7 @@ def trace_id(self, value): @property def span_id(self): # type: () -> str + """The span id of the currently executed span.""" if not self._span_id: self._span_id = uuid.uuid4().hex[16:] @@ -368,6 +386,9 @@ def span_id(self, value): def update(self, other_dict): # type: (Dict[str, Any]) -> None + """ + Updates the PropagationContext with data from the given dictionary. + """ for key, value in other_dict.items(): setattr(self, key, value) From acc5bd35bb740b3d29f1e1742ceaf99d10756db6 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Thu, 11 Apr 2024 16:40:00 +0200 Subject: [PATCH 03/10] nix From c84f653f6b748aef8d43c6e2427e8a90a2835223 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Thu, 11 Apr 2024 16:43:04 +0200 Subject: [PATCH 04/10] More verbose naming --- sentry_sdk/scope.py | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 3acd163673..2222e542b5 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -433,13 +433,13 @@ def _load_trace_data_from_env(self): def _extract_propagation_context(self, incoming_data): # type: (Dict[str, Any]) -> Optional[PropagationContext] - context = None + propagation_context = None normalized_data = normalize_incoming_data(incoming_data) baggage_header = normalized_data.get(BAGGAGE_HEADER_NAME) if baggage_header: - context = PropagationContext() - context.dynamic_sampling_context = Baggage.from_incoming_header( + propagation_context = PropagationContext() + propagation_context.dynamic_sampling_context = Baggage.from_incoming_header( baggage_header ).dynamic_sampling_context() @@ -447,11 +447,11 @@ def _extract_propagation_context(self, incoming_data): if sentry_trace_header: sentrytrace_data = extract_sentrytrace_data(sentry_trace_header) if sentrytrace_data is not None: - if context is None: - context = PropagationContext() - context.update(sentrytrace_data) + if propagation_context is None: + propagation_context = PropagationContext() + propagation_context.update(sentrytrace_data) - return context + return propagation_context def set_new_propagation_context(self): # type: () -> None @@ -469,10 +469,9 @@ def generate_propagation_context(self, incoming_data=None): if there is no `incoming_data` create new `_propagation_context`, but do NOT overwrite if already existing. """ if incoming_data: - context = self._extract_propagation_context(incoming_data) - - if context is not None: - self._propagation_context = context + propagation_context = self._extract_propagation_context(incoming_data) + if propagation_context is not None: + self._propagation_context = propagation_context if self._propagation_context is None and self._type != ScopeType.CURRENT: self.set_new_propagation_context() From 68662a7eac0612cf0bb909501712e486235d53ae Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Thu, 11 Apr 2024 17:12:26 +0200 Subject: [PATCH 05/10] formatting --- sentry_sdk/tracing_utils.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/sentry_sdk/tracing_utils.py b/sentry_sdk/tracing_utils.py index 5be27d5c3e..a5a07d485f 100644 --- a/sentry_sdk/tracing_utils.py +++ b/sentry_sdk/tracing_utils.py @@ -348,10 +348,13 @@ def __init__( """The span id of the currently executing span.""" self.parent_span_id = parent_span_id - """The id of the parent span that started this span. The parent span could also be a span in an upstream service.""" + """The id of the parent span that started this span. + The parent span could also be a span in an upstream service.""" self.parent_sampled = parent_sampled - """Boolean indicator if the parent span was sampled. Important when the parent span originated in an upstream service, because we watn to sample the whole trace, or nothing from the trace.""" + """Boolean indicator if the parent span was sampled. + Important when the parent span originated in an upstream service, + because we watn to sample the whole trace, or nothing from the trace.""" self.dynamic_sampling_context = dynamic_sampling_context """Data that is used for dynamic sampling decisions.""" From 466462c4791ec8e02e024b734db1a6a67389f056 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Fri, 12 Apr 2024 10:48:51 +0200 Subject: [PATCH 06/10] Added some tests --- sentry_sdk/tracing_utils.py | 5 +- tests/test_propagationcontext.py | 83 ++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 tests/test_propagationcontext.py diff --git a/sentry_sdk/tracing_utils.py b/sentry_sdk/tracing_utils.py index a5a07d485f..b626f529d4 100644 --- a/sentry_sdk/tracing_utils.py +++ b/sentry_sdk/tracing_utils.py @@ -393,7 +393,10 @@ def update(self, other_dict): Updates the PropagationContext with data from the given dictionary. """ for key, value in other_dict.items(): - setattr(self, key, value) + try: + setattr(self, key, value) + except AttributeError: + pass class Baggage: diff --git a/tests/test_propagationcontext.py b/tests/test_propagationcontext.py new file mode 100644 index 0000000000..545116e3a1 --- /dev/null +++ b/tests/test_propagationcontext.py @@ -0,0 +1,83 @@ +from sentry_sdk.tracing_utils import PropagationContext + + +def test_empty_context(): + ctx = PropagationContext() + + assert ctx.trace_id is not None + assert len(ctx.trace_id) == 32 + + assert ctx.span_id is not None + assert len(ctx.span_id) == 16 + + assert ctx.parent_span_id is None + assert ctx.parent_sampled is None + assert ctx.dynamic_sampling_context is None + + +def test_context_with_values(): + ctx = PropagationContext( + trace_id="1234567890abcdef1234567890abcdef", + span_id="1234567890abcdef", + parent_span_id="abcdef1234567890", + parent_sampled=True, + dynamic_sampling_context={ + "foo": "bar", + }, + ) + + assert ctx.trace_id == "1234567890abcdef1234567890abcdef" + assert ctx.span_id == "1234567890abcdef" + assert ctx.parent_span_id == "abcdef1234567890" + assert ctx.parent_sampled == True + assert ctx.dynamic_sampling_context == { + "foo": "bar", + } + + +def test_lacy_uuids(): + ctx = PropagationContext() + assert ctx._trace_id is None + assert ctx._span_id is None + + assert ctx.trace_id is not None # this sets _trace_id + assert ctx._trace_id is not None + assert ctx._span_id is None + + assert ctx.span_id is not None # this sets _span_id + assert ctx._trace_id is not None + assert ctx._span_id is not None + + +def test_property_setters(): + ctx = PropagationContext() + ctx.trace_id = "X234567890abcdef1234567890abcdef" + ctx.span_id = "X234567890abcdef" + + assert ctx._trace_id == "X234567890abcdef1234567890abcdef" + assert ctx.trace_id == "X234567890abcdef1234567890abcdef" + assert ctx._span_id == "X234567890abcdef" + assert ctx.span_id == "X234567890abcdef" + + +def test_update(): + ctx = PropagationContext() + + other_data = { + "trace_id": "Z234567890abcdef1234567890abcdef", + "parent_span_id": "Z234567890abcdef", + "parent_sampled": False, + "foo": "bar", + } + ctx.update(other_data) + + assert ctx._trace_id == "Z234567890abcdef1234567890abcdef" + assert ctx.trace_id == "Z234567890abcdef1234567890abcdef" + assert ctx._span_id is None # this will be set lazily + assert ctx.span_id is not None # this sets _span_id + assert ctx._span_id is not None + assert ctx.parent_span_id == "Z234567890abcdef" + assert ctx.parent_sampled == False + assert ctx.dynamic_sampling_context is None + + assert not hasattr(ctx, "foo") From 97a3329dffaf0d39e60b7b7a68c864c73332e2e6 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Fri, 12 Apr 2024 10:59:20 +0200 Subject: [PATCH 07/10] Moved into new class --- sentry_sdk/scope.py | 36 +++++++----------------------------- sentry_sdk/tracing_utils.py | 27 +++++++++++++++++++++++++++ 2 files changed, 34 insertions(+), 29 deletions(-) diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index 2222e542b5..cad4415ead 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -431,28 +431,6 @@ def _load_trace_data_from_env(self): return incoming_trace_information or None - def _extract_propagation_context(self, incoming_data): - # type: (Dict[str, Any]) -> Optional[PropagationContext] - propagation_context = None - - normalized_data = normalize_incoming_data(incoming_data) - baggage_header = normalized_data.get(BAGGAGE_HEADER_NAME) - if baggage_header: - propagation_context = PropagationContext() - propagation_context.dynamic_sampling_context = Baggage.from_incoming_header( - baggage_header - ).dynamic_sampling_context() - - sentry_trace_header = normalized_data.get(SENTRY_TRACE_HEADER_NAME) - if sentry_trace_header: - sentrytrace_data = extract_sentrytrace_data(sentry_trace_header) - if sentrytrace_data is not None: - if propagation_context is None: - propagation_context = PropagationContext() - propagation_context.update(sentrytrace_data) - - return propagation_context - def set_new_propagation_context(self): # type: () -> None """ @@ -463,18 +441,18 @@ def set_new_propagation_context(self): def generate_propagation_context(self, incoming_data=None): # type: (Optional[Dict[str, str]]) -> None """ - Makes sure the propagation context (`_propagation_context`) is set. - The propagation context only lives on the current scope. - If there is `incoming_data` overwrite existing `_propagation_context`. - if there is no `incoming_data` create new `_propagation_context`, but do NOT overwrite if already existing. + Makes sure the propagation context is set on the scope. + If there is `incoming_data` overwrite existing propagation context. + If there is no `incoming_data` create new propagation context, but do NOT overwrite if already existing. """ if incoming_data: - propagation_context = self._extract_propagation_context(incoming_data) + propagation_context = PropagationContext.from_incoming_data(incoming_data) if propagation_context is not None: self._propagation_context = propagation_context - if self._propagation_context is None and self._type != ScopeType.CURRENT: - self.set_new_propagation_context() + if self._type != ScopeType.CURRENT: + if self._propagation_context is None: + self.set_new_propagation_context() def get_dynamic_sampling_context(self): # type: () -> Optional[Dict[str, str]] diff --git a/sentry_sdk/tracing_utils.py b/sentry_sdk/tracing_utils.py index b626f529d4..d539cba15a 100644 --- a/sentry_sdk/tracing_utils.py +++ b/sentry_sdk/tracing_utils.py @@ -11,6 +11,10 @@ import sentry_sdk from sentry_sdk.consts import OP, SPANDATA +from sentry_sdk.tracing import ( + BAGGAGE_HEADER_NAME, + SENTRY_TRACE_HEADER_NAME, +) from sentry_sdk.utils import ( capture_internal_exceptions, filename_for_module, @@ -359,6 +363,29 @@ def __init__( self.dynamic_sampling_context = dynamic_sampling_context """Data that is used for dynamic sampling decisions.""" + @classmethod + def from_incoming_data(cls, incoming_data): + # type: (Dict[str, Any]) -> Optional[PropagationContext] + propagation_context = None + + normalized_data = normalize_incoming_data(incoming_data) + baggage_header = normalized_data.get(BAGGAGE_HEADER_NAME) + if baggage_header: + propagation_context = PropagationContext() + propagation_context.dynamic_sampling_context = Baggage.from_incoming_header( + baggage_header + ).dynamic_sampling_context() + + sentry_trace_header = normalized_data.get(SENTRY_TRACE_HEADER_NAME) + if sentry_trace_header: + sentrytrace_data = extract_sentrytrace_data(sentry_trace_header) + if sentrytrace_data is not None: + if propagation_context is None: + propagation_context = PropagationContext() + propagation_context.update(sentrytrace_data) + + return propagation_context + @property def trace_id(self): # type: () -> str From 8f1efc9435bd1b4aacae41d35ed75d0776359737 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Fri, 12 Apr 2024 11:08:17 +0200 Subject: [PATCH 08/10] linting --- sentry_sdk/scope.py | 1 - tests/test_propagationcontext.py | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/sentry_sdk/scope.py b/sentry_sdk/scope.py index cad4415ead..58686d56ef 100644 --- a/sentry_sdk/scope.py +++ b/sentry_sdk/scope.py @@ -14,7 +14,6 @@ from sentry_sdk.session import Session from sentry_sdk.tracing_utils import ( Baggage, - extract_sentrytrace_data, has_tracing_enabled, normalize_incoming_data, PropagationContext, diff --git a/tests/test_propagationcontext.py b/tests/test_propagationcontext.py index 545116e3a1..c650071511 100644 --- a/tests/test_propagationcontext.py +++ b/tests/test_propagationcontext.py @@ -29,7 +29,7 @@ def test_context_with_values(): assert ctx.trace_id == "1234567890abcdef1234567890abcdef" assert ctx.span_id == "1234567890abcdef" assert ctx.parent_span_id == "abcdef1234567890" - assert ctx.parent_sampled == True + assert ctx.parent_sampled assert ctx.dynamic_sampling_context == { "foo": "bar", } @@ -77,7 +77,7 @@ def test_update(): assert ctx.span_id is not None # this sets _span_id assert ctx._span_id is not None assert ctx.parent_span_id == "Z234567890abcdef" - assert ctx.parent_sampled == False + assert not ctx.parent_sampled assert ctx.dynamic_sampling_context is None assert not hasattr(ctx, "foo") From c7bf02fd72221e673c8d093f03b1bc14b83e8ab2 Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Fri, 12 Apr 2024 11:20:48 +0200 Subject: [PATCH 09/10] Fixed circular import --- sentry_sdk/tracing_utils.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/sentry_sdk/tracing_utils.py b/sentry_sdk/tracing_utils.py index d539cba15a..556a466c0b 100644 --- a/sentry_sdk/tracing_utils.py +++ b/sentry_sdk/tracing_utils.py @@ -11,10 +11,6 @@ import sentry_sdk from sentry_sdk.consts import OP, SPANDATA -from sentry_sdk.tracing import ( - BAGGAGE_HEADER_NAME, - SENTRY_TRACE_HEADER_NAME, -) from sentry_sdk.utils import ( capture_internal_exceptions, filename_for_module, @@ -676,7 +672,11 @@ def get_current_span(scope=None): # Circular imports -from sentry_sdk.tracing import LOW_QUALITY_TRANSACTION_SOURCES +from sentry_sdk.tracing import ( + BAGGAGE_HEADER_NAME, + LOW_QUALITY_TRANSACTION_SOURCES, + SENTRY_TRACE_HEADER_NAME, +) if TYPE_CHECKING: from sentry_sdk.tracing import Span From bce86c3925b60d4e8f5b29fcf1bf0e297abb1e5f Mon Sep 17 00:00:00 2001 From: Anton Pirker Date: Fri, 12 Apr 2024 11:21:36 +0200 Subject: [PATCH 10/10] nix