Skip to content

Commit ebde476

Browse files
authored
Put feature flags on isolation scope (#4363)
Feature flags should life on the isolation Scope. This has been first [implemented in SDK 3.0](#4353) and is now back ported to 2.x.
1 parent 18a1104 commit ebde476

File tree

6 files changed

+115
-2
lines changed

6 files changed

+115
-2
lines changed

docs/api.rst

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ Capturing Data
2525
Enriching Events
2626
================
2727

28+
.. autofunction:: sentry_sdk.api.add_attachment
2829
.. autofunction:: sentry_sdk.api.add_breadcrumb
2930
.. autofunction:: sentry_sdk.api.set_context
3031
.. autofunction:: sentry_sdk.api.set_extra
@@ -63,4 +64,3 @@ Managing Scope (advanced)
6364
.. autofunction:: sentry_sdk.api.push_scope
6465

6566
.. autofunction:: sentry_sdk.api.new_scope
66-

sentry_sdk/__init__.py

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"integrations",
1616
# From sentry_sdk.api
1717
"init",
18+
"add_attachment",
1819
"add_breadcrumb",
1920
"capture_event",
2021
"capture_exception",

sentry_sdk/api.py

+15
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ def overload(x):
5151
# When changing this, update __all__ in __init__.py too
5252
__all__ = [
5353
"init",
54+
"add_attachment",
5455
"add_breadcrumb",
5556
"capture_event",
5657
"capture_exception",
@@ -184,6 +185,20 @@ def capture_exception(
184185
return get_current_scope().capture_exception(error, scope=scope, **scope_kwargs)
185186

186187

188+
@scopemethod
189+
def add_attachment(
190+
bytes=None, # type: Union[None, bytes, Callable[[], bytes]]
191+
filename=None, # type: Optional[str]
192+
path=None, # type: Optional[str]
193+
content_type=None, # type: Optional[str]
194+
add_to_transactions=False, # type: bool
195+
):
196+
# type: (...) -> None
197+
return get_isolation_scope().add_attachment(
198+
bytes, filename, path, content_type, add_to_transactions
199+
)
200+
201+
187202
@scopemethod
188203
def add_breadcrumb(
189204
crumb=None, # type: Optional[Breadcrumb]

sentry_sdk/feature_flags.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ def add_feature_flag(flag, result):
6464
Records a flag and its value to be sent on subsequent error events.
6565
We recommend you do this on flag evaluations. Flags are buffered per Sentry scope.
6666
"""
67-
flags = sentry_sdk.get_current_scope().flags
67+
flags = sentry_sdk.get_isolation_scope().flags
6868
flags.set(flag, result)
6969

7070
span = sentry_sdk.get_current_span()

tests/integrations/fastapi/test_fastapi.py

+40
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
from fastapi.testclient import TestClient
1111
from fastapi.middleware.trustedhost import TrustedHostMiddleware
1212

13+
import sentry_sdk
1314
from sentry_sdk import capture_message
15+
from sentry_sdk.feature_flags import add_feature_flag
1416
from sentry_sdk.integrations.asgi import SentryAsgiMiddleware
1517
from sentry_sdk.integrations.fastapi import FastApiIntegration
1618
from sentry_sdk.integrations.starlette import StarletteIntegration
@@ -714,3 +716,41 @@ async def subapp_route():
714716
assert event["transaction"] == "/subapp"
715717
else:
716718
assert event["transaction"].endswith("subapp_route")
719+
720+
721+
@pytest.mark.asyncio
722+
async def test_feature_flags(sentry_init, capture_events):
723+
sentry_init(
724+
traces_sample_rate=1.0,
725+
integrations=[StarletteIntegration(), FastApiIntegration()],
726+
)
727+
728+
events = capture_events()
729+
730+
app = FastAPI()
731+
732+
@app.get("/error")
733+
async def _error():
734+
add_feature_flag("hello", False)
735+
736+
with sentry_sdk.start_span(name="test-span"):
737+
with sentry_sdk.start_span(name="test-span-2"):
738+
raise ValueError("something is wrong!")
739+
740+
try:
741+
client = TestClient(app)
742+
client.get("/error")
743+
except ValueError:
744+
pass
745+
746+
found = False
747+
for event in events:
748+
if "exception" in event.keys():
749+
assert event["contexts"]["flags"] == {
750+
"values": [
751+
{"flag": "hello", "result": False},
752+
]
753+
}
754+
found = True
755+
756+
assert found, "No event with exception found"

tests/test_feature_flags.py

+57
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,63 @@ def test_featureflags_integration(sentry_init, capture_events, uninstall_integra
3131
}
3232

3333

34+
@pytest.mark.asyncio
35+
async def test_featureflags_integration_spans_async(sentry_init, capture_events):
36+
sentry_init(
37+
traces_sample_rate=1.0,
38+
)
39+
events = capture_events()
40+
41+
add_feature_flag("hello", False)
42+
43+
try:
44+
with sentry_sdk.start_span(name="test-span"):
45+
with sentry_sdk.start_span(name="test-span-2"):
46+
raise ValueError("something wrong!")
47+
except ValueError as e:
48+
sentry_sdk.capture_exception(e)
49+
50+
found = False
51+
for event in events:
52+
if "exception" in event.keys():
53+
assert event["contexts"]["flags"] == {
54+
"values": [
55+
{"flag": "hello", "result": False},
56+
]
57+
}
58+
found = True
59+
60+
assert found, "No event with exception found"
61+
62+
63+
def test_featureflags_integration_spans_sync(sentry_init, capture_events):
64+
sentry_init(
65+
traces_sample_rate=1.0,
66+
)
67+
events = capture_events()
68+
69+
add_feature_flag("hello", False)
70+
71+
try:
72+
with sentry_sdk.start_span(name="test-span"):
73+
with sentry_sdk.start_span(name="test-span-2"):
74+
raise ValueError("something wrong!")
75+
except ValueError as e:
76+
sentry_sdk.capture_exception(e)
77+
78+
found = False
79+
for event in events:
80+
if "exception" in event.keys():
81+
assert event["contexts"]["flags"] == {
82+
"values": [
83+
{"flag": "hello", "result": False},
84+
]
85+
}
86+
found = True
87+
88+
assert found, "No event with exception found"
89+
90+
3491
def test_featureflags_integration_threaded(
3592
sentry_init, capture_events, uninstall_integration
3693
):

0 commit comments

Comments
 (0)