Skip to content

Commit e68161c

Browse files
Load tracing information from environment (#2176)
It should be able to continue a trace with trace information that was given to the python process over environment variables. See this RFC for the spec: https://github.com/getsentry/rfcs/blob/main/text/0071-continue-trace-over-process-boundaries.md --------- Co-authored-by: Ivana Kellyerova <[email protected]>
1 parent c26f35a commit e68161c

File tree

5 files changed

+147
-3
lines changed

5 files changed

+147
-3
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ lint: .venv
5151
apidocs: .venv
5252
@$(VENV_PATH)/bin/pip install --editable .
5353
@$(VENV_PATH)/bin/pip install -U -r ./docs-requirements.txt
54-
@$(VENV_PATH)/bin/sphinx-build -W -b html docs/ docs/_build
54+
@$(VENV_PATH)/bin/sphinx-build -vv -W -b html docs/ docs/_build
5555
.PHONY: apidocs
5656

5757
apidocs-hotfix: apidocs

sentry_sdk/_compat.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,10 @@ def check_thread_support():
8282
if "threads" in opt:
8383
return
8484

85-
if str(opt.get("enable-threads", "0")).lower() in ("false", "off", "no", "0"):
85+
# put here because of circular import
86+
from sentry_sdk.consts import FALSE_VALUES
87+
88+
if str(opt.get("enable-threads", "0")).lower() in FALSE_VALUES:
8689
from warnings import warn
8790

8891
warn(

sentry_sdk/consts.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,14 @@
4545

4646
MATCH_ALL = r".*"
4747

48+
FALSE_VALUES = [
49+
"false",
50+
"no",
51+
"off",
52+
"n",
53+
"0",
54+
]
55+
4856

4957
class INSTRUMENTER:
5058
SENTRY = "sentry"

sentry_sdk/scope.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from copy import copy
22
from collections import deque
33
from itertools import chain
4+
import os
45
import uuid
56

67
from sentry_sdk.attachments import Attachment
@@ -19,6 +20,8 @@
1920
from sentry_sdk._types import TYPE_CHECKING
2021
from sentry_sdk.utils import logger, capture_internal_exceptions
2122

23+
from sentry_sdk.consts import FALSE_VALUES
24+
2225

2326
if TYPE_CHECKING:
2427
from typing import Any
@@ -122,7 +125,36 @@ def __init__(self):
122125
self._propagation_context = None # type: Optional[Dict[str, Any]]
123126

124127
self.clear()
125-
self.generate_propagation_context()
128+
129+
incoming_trace_information = self._load_trace_data_from_env()
130+
self.generate_propagation_context(incoming_data=incoming_trace_information)
131+
132+
def _load_trace_data_from_env(self):
133+
# type: () -> Optional[Dict[str, str]]
134+
"""
135+
Load Sentry trace id and baggage from environment variables.
136+
Can be disabled by setting SENTRY_USE_ENVIRONMENT to "false".
137+
"""
138+
incoming_trace_information = None
139+
140+
sentry_use_environment = (
141+
os.environ.get("SENTRY_USE_ENVIRONMENT") or ""
142+
).lower()
143+
use_environment = sentry_use_environment not in FALSE_VALUES
144+
if use_environment:
145+
incoming_trace_information = {}
146+
147+
if os.environ.get("SENTRY_TRACE"):
148+
incoming_trace_information[SENTRY_TRACE_HEADER_NAME] = (
149+
os.environ.get("SENTRY_TRACE") or ""
150+
)
151+
152+
if os.environ.get("SENTRY_BAGGAGE"):
153+
incoming_trace_information[BAGGAGE_HEADER_NAME] = (
154+
os.environ.get("SENTRY_BAGGAGE") or ""
155+
)
156+
157+
return incoming_trace_information or None
126158

127159
def _extract_propagation_context(self, data):
128160
# type: (Dict[str, Any]) -> Optional[Dict[str, Any]]
@@ -141,6 +173,12 @@ def _extract_propagation_context(self, data):
141173
if sentrytrace_data is not None:
142174
context.update(sentrytrace_data)
143175

176+
only_baggage_no_sentry_trace = (
177+
"dynamic_sampling_context" in context and "trace_id" not in context
178+
)
179+
if only_baggage_no_sentry_trace:
180+
context.update(self._create_new_propagation_context())
181+
144182
if context:
145183
if not context.get("span_id"):
146184
context["span_id"] = uuid.uuid4().hex[16:]

tests/test_scope.py

Lines changed: 95 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
import copy
2+
import os
3+
import pytest
24
from sentry_sdk import capture_exception
35
from sentry_sdk.scope import Scope
46

7+
try:
8+
from unittest import mock # python 3.3 and above
9+
except ImportError:
10+
import mock # python < 3.3
11+
512

613
def test_copying():
714
s1 = Scope()
@@ -62,3 +69,91 @@ def test_common_args():
6269
assert s2._extras == {"k": "v", "foo": "bar"}
6370
assert s2._tags == {"a": "b", "x": "y"}
6471
assert s2._contexts == {"os": {"name": "Blafasel"}, "device": {"a": "b"}}
72+
73+
74+
BAGGAGE_VALUE = (
75+
"other-vendor-value-1=foo;bar;baz, sentry-trace_id=771a43a4192642f0b136d5159a501700, "
76+
"sentry-public_key=49d0f7386ad645858ae85020e393bef3, sentry-sample_rate=0.01337, "
77+
"sentry-user_id=Am%C3%A9lie, other-vendor-value-2=foo;bar;"
78+
)
79+
80+
SENTRY_TRACE_VALUE = "771a43a4192642f0b136d5159a501700-1234567890abcdef-1"
81+
82+
83+
@pytest.mark.parametrize(
84+
"env,excepted_value",
85+
[
86+
(
87+
{
88+
"SENTRY_TRACE": SENTRY_TRACE_VALUE,
89+
},
90+
{
91+
"sentry-trace": SENTRY_TRACE_VALUE,
92+
},
93+
),
94+
(
95+
{
96+
"SENTRY_BAGGAGE": BAGGAGE_VALUE,
97+
},
98+
{
99+
"baggage": BAGGAGE_VALUE,
100+
},
101+
),
102+
(
103+
{
104+
"SENTRY_TRACE": SENTRY_TRACE_VALUE,
105+
"SENTRY_BAGGAGE": BAGGAGE_VALUE,
106+
},
107+
{
108+
"sentry-trace": SENTRY_TRACE_VALUE,
109+
"baggage": BAGGAGE_VALUE,
110+
},
111+
),
112+
(
113+
{
114+
"SENTRY_USE_ENVIRONMENT": "",
115+
"SENTRY_TRACE": SENTRY_TRACE_VALUE,
116+
"SENTRY_BAGGAGE": BAGGAGE_VALUE,
117+
},
118+
{
119+
"sentry-trace": SENTRY_TRACE_VALUE,
120+
"baggage": BAGGAGE_VALUE,
121+
},
122+
),
123+
(
124+
{
125+
"SENTRY_USE_ENVIRONMENT": "True",
126+
"SENTRY_TRACE": SENTRY_TRACE_VALUE,
127+
"SENTRY_BAGGAGE": BAGGAGE_VALUE,
128+
},
129+
{
130+
"sentry-trace": SENTRY_TRACE_VALUE,
131+
"baggage": BAGGAGE_VALUE,
132+
},
133+
),
134+
(
135+
{
136+
"SENTRY_USE_ENVIRONMENT": "no",
137+
"SENTRY_TRACE": SENTRY_TRACE_VALUE,
138+
"SENTRY_BAGGAGE": BAGGAGE_VALUE,
139+
},
140+
None,
141+
),
142+
(
143+
{
144+
"SENTRY_USE_ENVIRONMENT": "True",
145+
"MY_OTHER_VALUE": "asdf",
146+
"SENTRY_RELEASE": "1.0.0",
147+
},
148+
None,
149+
),
150+
],
151+
)
152+
def test_load_trace_data_from_env(env, excepted_value):
153+
new_env = os.environ.copy()
154+
new_env.update(env)
155+
156+
with mock.patch.dict(os.environ, new_env):
157+
s = Scope()
158+
incoming_trace_data = s._load_trace_data_from_env()
159+
assert incoming_trace_data == excepted_value

0 commit comments

Comments
 (0)