Skip to content

Commit 269d96d

Browse files
BYKBurak Yigit Kaya
andauthored
feat: Add SENTRY_SPOTLIGHT env variable support (#3443)
Allows setting Spotlight through `$SENTRY_SPOTLIGHT` env variable. --------- Co-authored-by: Burak Yigit Kaya <[email protected]>
1 parent aee87fb commit 269d96d

File tree

5 files changed

+150
-7
lines changed

5 files changed

+150
-7
lines changed

sentry_sdk/client.py

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99

1010
from sentry_sdk._compat import PY37, check_uwsgi_thread_support
1111
from sentry_sdk.utils import (
12+
ContextVar,
1213
capture_internal_exceptions,
1314
current_stacktrace,
15+
env_to_bool,
1416
format_timestamp,
1517
get_sdk_name,
1618
get_type_name,
@@ -30,7 +32,6 @@
3032
ClientConstructor,
3133
)
3234
from sentry_sdk.integrations import _DEFAULT_INTEGRATIONS, setup_integrations
33-
from sentry_sdk.utils import ContextVar
3435
from sentry_sdk.sessions import SessionFlusher
3536
from sentry_sdk.envelope import Envelope
3637
from sentry_sdk.profiler.continuous_profiler import setup_continuous_profiler
@@ -104,11 +105,7 @@ def _get_options(*args, **kwargs):
104105
rv["environment"] = os.environ.get("SENTRY_ENVIRONMENT") or "production"
105106

106107
if rv["debug"] is None:
107-
rv["debug"] = os.environ.get("SENTRY_DEBUG", "False").lower() in (
108-
"true",
109-
"1",
110-
"t",
111-
)
108+
rv["debug"] = env_to_bool(os.environ.get("SENTRY_DEBUG", "False"), strict=True)
112109

113110
if rv["server_name"] is None and hasattr(socket, "gethostname"):
114111
rv["server_name"] = socket.gethostname()
@@ -375,6 +372,16 @@ def _capture_envelope(envelope):
375372
)
376373

377374
self.spotlight = None
375+
spotlight_config = self.options.get("spotlight")
376+
if spotlight_config is None and "SENTRY_SPOTLIGHT" in os.environ:
377+
spotlight_env_value = os.environ["SENTRY_SPOTLIGHT"]
378+
spotlight_config = env_to_bool(spotlight_env_value, strict=True)
379+
self.options["spotlight"] = (
380+
spotlight_config
381+
if spotlight_config is not None
382+
else spotlight_env_value
383+
)
384+
378385
if self.options.get("spotlight"):
379386
self.spotlight = setup_spotlight(self.options)
380387

sentry_sdk/spotlight.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,9 @@
1212
from sentry_sdk.envelope import Envelope
1313

1414

15+
DEFAULT_SPOTLIGHT_URL = "http://localhost:8969/stream"
16+
17+
1518
class SpotlightClient:
1619
def __init__(self, url):
1720
# type: (str) -> None
@@ -51,7 +54,7 @@ def setup_spotlight(options):
5154
if isinstance(url, str):
5255
pass
5356
elif url is True:
54-
url = "http://localhost:8969/stream"
57+
url = DEFAULT_SPOTLIGHT_URL
5558
else:
5659
return None
5760

sentry_sdk/utils.py

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,25 @@
7171

7272
SENSITIVE_DATA_SUBSTITUTE = "[Filtered]"
7373

74+
FALSY_ENV_VALUES = frozenset(("false", "f", "n", "no", "off", "0"))
75+
TRUTHY_ENV_VALUES = frozenset(("true", "t", "y", "yes", "on", "1"))
76+
77+
78+
def env_to_bool(value, *, strict=False):
79+
# type: (Any, Optional[bool]) -> bool | None
80+
"""Casts an ENV variable value to boolean using the constants defined above.
81+
In strict mode, it may return None if the value doesn't match any of the predefined values.
82+
"""
83+
normalized = str(value).lower() if value is not None else None
84+
85+
if normalized in FALSY_ENV_VALUES:
86+
return False
87+
88+
if normalized in TRUTHY_ENV_VALUES:
89+
return True
90+
91+
return None if strict else bool(value)
92+
7493

7594
def json_dumps(data):
7695
# type: (Any) -> bytes

tests/test_client.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
capture_event,
2222
set_tag,
2323
)
24+
from sentry_sdk.spotlight import DEFAULT_SPOTLIGHT_URL
2425
from sentry_sdk.utils import capture_internal_exception
2526
from sentry_sdk.integrations.executing import ExecutingIntegration
2627
from sentry_sdk.transport import Transport
@@ -1097,6 +1098,47 @@ def test_debug_option(
10971098
assert "something is wrong" not in caplog.text
10981099

10991100

1101+
@pytest.mark.parametrize(
1102+
"client_option,env_var_value,spotlight_url_expected",
1103+
[
1104+
(None, None, None),
1105+
(None, "", None),
1106+
(None, "F", None),
1107+
(False, None, None),
1108+
(False, "", None),
1109+
(False, "t", None),
1110+
(None, "t", DEFAULT_SPOTLIGHT_URL),
1111+
(None, "1", DEFAULT_SPOTLIGHT_URL),
1112+
(True, None, DEFAULT_SPOTLIGHT_URL),
1113+
(True, "http://localhost:8080/slurp", DEFAULT_SPOTLIGHT_URL),
1114+
("http://localhost:8080/slurp", "f", "http://localhost:8080/slurp"),
1115+
(None, "http://localhost:8080/slurp", "http://localhost:8080/slurp"),
1116+
],
1117+
)
1118+
def test_spotlight_option(
1119+
sentry_init,
1120+
monkeypatch,
1121+
client_option,
1122+
env_var_value,
1123+
spotlight_url_expected,
1124+
):
1125+
if env_var_value is None:
1126+
monkeypatch.delenv("SENTRY_SPOTLIGHT", raising=False)
1127+
else:
1128+
monkeypatch.setenv("SENTRY_SPOTLIGHT", env_var_value)
1129+
1130+
if client_option is None:
1131+
sentry_init()
1132+
else:
1133+
sentry_init(spotlight=client_option)
1134+
1135+
client = sentry_sdk.get_client()
1136+
url = client.spotlight.url if client.spotlight else None
1137+
assert (
1138+
url == spotlight_url_expected
1139+
), f"With config {client_option} and env {env_var_value}"
1140+
1141+
11001142
class IssuesSamplerTestConfig:
11011143
def __init__(
11021144
self,

tests/test_utils.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from sentry_sdk.utils import (
1313
Components,
1414
Dsn,
15+
env_to_bool,
1516
get_current_thread_meta,
1617
get_default_release,
1718
get_error_message,
@@ -59,6 +60,77 @@ def _normalize_distribution_name(name):
5960
return re.sub(r"[-_.]+", "-", name).lower()
6061

6162

63+
@pytest.mark.parametrize(
64+
"env_var_value,strict,expected",
65+
[
66+
(None, True, None),
67+
(None, False, False),
68+
("", True, None),
69+
("", False, False),
70+
("t", True, True),
71+
("T", True, True),
72+
("t", False, True),
73+
("T", False, True),
74+
("y", True, True),
75+
("Y", True, True),
76+
("y", False, True),
77+
("Y", False, True),
78+
("1", True, True),
79+
("1", False, True),
80+
("True", True, True),
81+
("True", False, True),
82+
("true", True, True),
83+
("true", False, True),
84+
("tRuE", True, True),
85+
("tRuE", False, True),
86+
("Yes", True, True),
87+
("Yes", False, True),
88+
("yes", True, True),
89+
("yes", False, True),
90+
("yEs", True, True),
91+
("yEs", False, True),
92+
("On", True, True),
93+
("On", False, True),
94+
("on", True, True),
95+
("on", False, True),
96+
("oN", True, True),
97+
("oN", False, True),
98+
("f", True, False),
99+
("f", False, False),
100+
("n", True, False),
101+
("N", True, False),
102+
("n", False, False),
103+
("N", False, False),
104+
("0", True, False),
105+
("0", False, False),
106+
("False", True, False),
107+
("False", False, False),
108+
("false", True, False),
109+
("false", False, False),
110+
("FaLsE", True, False),
111+
("FaLsE", False, False),
112+
("No", True, False),
113+
("No", False, False),
114+
("no", True, False),
115+
("no", False, False),
116+
("nO", True, False),
117+
("nO", False, False),
118+
("Off", True, False),
119+
("Off", False, False),
120+
("off", True, False),
121+
("off", False, False),
122+
("oFf", True, False),
123+
("oFf", False, False),
124+
("xxx", True, None),
125+
("xxx", False, True),
126+
],
127+
)
128+
def test_env_to_bool(env_var_value, strict, expected):
129+
assert (
130+
env_to_bool(env_var_value, strict=strict) == expected
131+
), f"Value: {env_var_value}, strict: {strict}"
132+
133+
62134
@pytest.mark.parametrize(
63135
("url", "expected_result"),
64136
[

0 commit comments

Comments
 (0)