Skip to content

Commit 3814acc

Browse files
committed
Baggage creation for head of trace
1 parent fa4f5b0 commit 3814acc

File tree

2 files changed

+67
-7
lines changed

2 files changed

+67
-7
lines changed

sentry_sdk/tracing.py

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,10 @@ def continue_from_headers(
281281

282282
if sentrytrace_kwargs is not None:
283283
kwargs.update(sentrytrace_kwargs)
284+
285+
# If there's an incoming sentry-trace but no incoming baggage header,
286+
# for instance in traces coming from older SDKs,
287+
# baggage will be empty and immutable and won't be populated as head SDK.
284288
baggage.freeze()
285289

286290
kwargs.update(extract_tracestate_data(headers.get("tracestate")))
@@ -309,8 +313,8 @@ def iter_headers(self):
309313
if tracestate:
310314
yield "tracestate", tracestate
311315

312-
if self.containing_transaction and self.containing_transaction._baggage:
313-
baggage = self.containing_transaction._baggage.serialize()
316+
if self.containing_transaction:
317+
baggage = self.containing_transaction.get_baggage().serialize()
314318
if baggage:
315319
yield "baggage", baggage
316320

@@ -513,11 +517,10 @@ def get_trace_context(self):
513517
if sentry_tracestate:
514518
rv["tracestate"] = sentry_tracestate
515519

516-
# TODO-neel populate fresh if head SDK
517-
if self.containing_transaction and self.containing_transaction._baggage:
520+
if self.containing_transaction:
518521
rv[
519522
"dynamic_sampling_context"
520-
] = self.containing_transaction._baggage.dynamic_sampling_context()
523+
] = self.containing_transaction.get_baggage().dynamic_sampling_context()
521524

522525
return rv
523526

@@ -527,6 +530,7 @@ class Transaction(Span):
527530
"name",
528531
"source",
529532
"parent_sampled",
533+
"sample_rate",
530534
# the sentry portion of the `tracestate` header used to transmit
531535
# correlation context for server-side dynamic sampling, of the form
532536
# `sentry=xxxxx`, where `xxxxx` is the base64-encoded json of the
@@ -562,6 +566,7 @@ def __init__(
562566
Span.__init__(self, **kwargs)
563567
self.name = name
564568
self.source = source
569+
self.sample_rate = None # type: Optional[float]
565570
self.parent_sampled = parent_sampled
566571
# if tracestate isn't inherited and set here, it will get set lazily,
567572
# either the first time an outgoing request needs it for a header or the
@@ -570,7 +575,7 @@ def __init__(
570575
self._third_party_tracestate = third_party_tracestate
571576
self._measurements = {} # type: Dict[str, Any]
572577
self._profile = None # type: Optional[Sampler]
573-
self._baggage = baggage
578+
self._baggage = baggage # type: Optional[Baggage]
574579

575580
def __repr__(self):
576581
# type: () -> str
@@ -708,6 +713,17 @@ def to_json(self):
708713

709714
return rv
710715

716+
def get_baggage(self):
717+
# type: () -> Baggage
718+
"""
719+
The first time a new baggage with sentry items is made,
720+
it will be frozen.
721+
"""
722+
if not self._baggage or self._baggage.mutable:
723+
self._baggage = Baggage.populate_from_transaction(self)
724+
725+
return self._baggage
726+
711727
def _set_initial_sampling_decision(self, sampling_context):
712728
# type: (SamplingContext) -> None
713729
"""
@@ -773,6 +789,9 @@ def _set_initial_sampling_decision(self, sampling_context):
773789
self.sampled = False
774790
return
775791

792+
# used to create baggage value for head SDKs in dynamic sampling
793+
self.sample_rate = float(sample_rate)
794+
776795
# if the function returned 0 (or false), or if `traces_sample_rate` is
777796
# 0, it's a sign the transaction should be dropped
778797
if not sample_rate:

sentry_sdk/tracing_utils.py

Lines changed: 42 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -470,6 +470,46 @@ def from_incoming_header(cls, header):
470470

471471
return Baggage(sentry_items, third_party_items, mutable)
472472

473+
@classmethod
474+
def populate_from_transaction(cls, transaction):
475+
# type: (Transaction) -> Baggage
476+
"""
477+
Populate fresh baggage entry with sentry_items and make it immutable
478+
if this is the head SDK which originates traces.
479+
"""
480+
481+
hub = transaction.hub or sentry_sdk.Hub.current
482+
client = hub.client
483+
sentry_items = {} # type: Dict[str, str]
484+
485+
if not client:
486+
return Baggage(sentry_items)
487+
488+
options = client.options or {}
489+
user = (hub.scope and hub.scope._user) or {}
490+
491+
sentry_items["trace_id"] = transaction.trace_id
492+
493+
if options.get("environment"):
494+
sentry_items["environment"] = options["environment"]
495+
496+
if options.get("release"):
497+
sentry_items["release"] = options["release"]
498+
499+
if options.get("dsn"):
500+
sentry_items["public_key"] = Dsn(options["dsn"]).public_key
501+
502+
if transaction.name and transaction.source != TRANSACTION_SOURCE_URL:
503+
sentry_items["transaction"] = transaction.name
504+
505+
if user.get("segment"):
506+
sentry_items["user_segment"] = user["segment"]
507+
508+
if transaction.sample_rate is not None:
509+
sentry_items["sample_rate"] = str(transaction.sample_rate)
510+
511+
return Baggage(sentry_items, mutable=False)
512+
473513
def freeze(self):
474514
# type: () -> None
475515
self.mutable = False
@@ -500,6 +540,7 @@ def serialize(self, include_third_party=False):
500540

501541

502542
# Circular imports
543+
from sentry_sdk.tracing import TRANSACTION_SOURCE_URL
503544

504545
if MYPY:
505-
from sentry_sdk.tracing import Span
546+
from sentry_sdk.tracing import Span, Transaction

0 commit comments

Comments
 (0)