Skip to content

Commit e960164

Browse files
committed
Add ability to enable/disable the SDK (#26)
Revision 2 Changes: - SDKConfig now built on top of recorder. SDK-level configuration should all be done through SDKConfig. - Renamed the SDK Enable environment variable to AWS_XRAY_SDK_ENABLED - Disabling the SDK now causes LambdaContext to set sampling decision of facade segment to False. Previously, it would force all subsegments to be generated as dummy subsegments. - SDKConfig.set_sdk_enabled(value) method defaults to True and throws an exception if value is not of type boolean.
1 parent 7968a17 commit e960164

File tree

12 files changed

+101
-95
lines changed

12 files changed

+101
-95
lines changed

aws_xray_sdk/__init__.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
from .sdk_config import SDKConfig
2+
3+
global_sdk_config = SDKConfig()

aws_xray_sdk/core/lambda_launcher.py

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@
22
import logging
33
import threading
44

5-
from aws_xray_sdk.sdk_config import SDKConfig
5+
import aws_xray_sdk
66
from .models.facade_segment import FacadeSegment
77
from .models.trace_header import TraceHeader
8-
from .models.dummy_entities import DummySubsegment
98
from .context import Context
109

1110
log = logging.getLogger(__name__)
@@ -72,14 +71,10 @@ def put_subsegment(self, subsegment):
7271
current_entity = self.get_trace_entity()
7372

7473
if not self._is_subsegment(current_entity) and current_entity.initializing:
75-
log.warning("Subsegment %s discarded due to Lambda worker still initializing" % subsegment.name)
74+
if sdk_config_module.sdk_enabled():
75+
log.warning("Subsegment %s discarded due to Lambda worker still initializing" % subsegment.name)
7676
return
7777

78-
enabled = SDKConfig.sdk_enabled()
79-
if not enabled:
80-
# For lambda, if the SDK is not enabled, we force the subsegment to be a dummy segment.
81-
subsegment = DummySubsegment(current_entity)
82-
8378
current_entity.add_subsegment(subsegment)
8479
self._local.entities.append(subsegment)
8580

@@ -99,6 +94,9 @@ def _refresh_context(self):
9994
"""
10095
header_str = os.getenv(LAMBDA_TRACE_HEADER_KEY)
10196
trace_header = TraceHeader.from_header_str(header_str)
97+
if not aws_xray_sdk.global_sdk_config.sdk_enabled():
98+
trace_header._sampled = False
99+
102100
segment = getattr(self._local, 'segment', None)
103101

104102
if segment:
@@ -130,7 +128,10 @@ def _initialize_context(self, trace_header):
130128
set by AWS Lambda and initialize storage for subsegments.
131129
"""
132130
sampled = None
133-
if trace_header.sampled == 0:
131+
if not aws_xray_sdk.global_sdk_config.sdk_enabled():
132+
# Force subsequent subsegments to be disabled and turned into DummySegments.
133+
sampled = False
134+
elif trace_header.sampled == 0:
134135
sampled = False
135136
elif trace_header.sampled == 1:
136137
sampled = True

aws_xray_sdk/core/patcher.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import sys
88
import wrapt
99

10-
from aws_xray_sdk.sdk_config import SDKConfig
10+
import aws_xray_sdk
1111
from .utils.compat import PY2, is_classmethod, is_instance_method
1212

1313
log = logging.getLogger(__name__)
@@ -61,7 +61,7 @@ def _is_valid_import(module):
6161

6262

6363
def patch(modules_to_patch, raise_errors=True, ignore_module_patterns=None):
64-
enabled = SDKConfig.sdk_enabled()
64+
enabled = aws_xray_sdk.global_sdk_config.sdk_enabled()
6565
if not enabled:
6666
return # Disable module patching if the SDK is disabled.
6767
modules = set()

aws_xray_sdk/core/recorder.py

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import time
77

88
from aws_xray_sdk.version import VERSION
9-
from aws_xray_sdk.sdk_config import SDKConfig
109
from .models.segment import Segment, SegmentContextManager
1110
from .models.subsegment import Subsegment, SubsegmentContextManager
1211
from .models.default_dynamic_naming import DefaultDynamicNaming
@@ -178,11 +177,7 @@ class to have your own implementation of the streaming process.
178177
if stream_sql is not None:
179178
self.stream_sql = stream_sql
180179
if enabled is not None:
181-
SDKConfig.set_sdk_enabled(enabled)
182-
else:
183-
# By default we enable if no enable parameter is given. Prevents unit tests from breaking
184-
# if setup doesn't explicitly set enabled while other tests set enabled to false.
185-
SDKConfig.set_sdk_enabled(True)
180+
self.enabled = enabled
186181

187182
if plugins:
188183
plugin_modules = get_plugin_modules(plugins)
@@ -241,7 +236,7 @@ def begin_segment(self, name=None, traceid=None,
241236
# To disable the recorder, we set the sampling decision to always be false.
242237
# This way, when segments are generated, they become dummy segments and are ultimately never sent.
243238
# The call to self._sampler.should_trace() is never called either so the poller threads are never started.
244-
if not SDKConfig.sdk_enabled():
239+
if not self.enabled:
245240
sampling = 0
246241

247242
# we respect the input sampling decision
@@ -425,7 +420,7 @@ def record_subsegment(self, wrapped, instance, args, kwargs, name,
425420
# In the case when the SDK is disabled, we ensure that a parent segment exists, because this is usually
426421
# handled by the middleware. We generate a dummy segment as the parent segment if one doesn't exist.
427422
# This is to allow potential segment method calls to not throw exceptions in the captured method.
428-
if not SDKConfig.sdk_enabled():
423+
if not self.enabled:
429424
try:
430425
self.current_segment()
431426
except SegmentNotFoundException:

aws_xray_sdk/core/sampling/sampler.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from .target_poller import TargetPoller
1010
from .connector import ServiceConnector
1111
from .reservoir import ReservoirDecision
12+
import aws_xray_sdk
1213

1314
log = logging.getLogger(__name__)
1415

@@ -37,6 +38,9 @@ def start(self):
3738
Start rule poller and target poller once X-Ray daemon address
3839
and context manager is in place.
3940
"""
41+
if not aws_xray_sdk.global_sdk_config.sdk_enabled():
42+
return
43+
4044
with self._lock:
4145
if not self._started:
4246
self._rule_poller.start()
@@ -51,6 +55,9 @@ def should_trace(self, sampling_req=None):
5155
All optional arguments are extracted from incoming requests by
5256
X-Ray middleware to perform path based sampling.
5357
"""
58+
if not aws_xray_sdk.global_sdk_config.sdk_enabled():
59+
return False
60+
5461
if not self._started:
5562
self.start() # only front-end that actually uses the sampler spawns poller threads
5663

aws_xray_sdk/sdk_config.py

Lines changed: 26 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,12 @@
11
import os
2+
import aws_xray_sdk.core
3+
4+
5+
class InvalidParameterTypeException(Exception):
6+
"""
7+
Exception thrown when an invalid parameter is passed into SDKConfig.set_sdk_enabled.
8+
"""
9+
pass
210

311

412
class SDKConfig(object):
@@ -7,18 +15,18 @@ class SDKConfig(object):
715
It is recommended to only use the recorder to set this configuration's enabled
816
flag to maintain thread safety.
917
"""
10-
XRAY_ENABLED_KEY = 'AWS_XRAY_ENABLED'
18+
XRAY_ENABLED_KEY = 'AWS_XRAY_SDK_ENABLED'
1119
__SDK_ENABLED = str(os.getenv(XRAY_ENABLED_KEY, 'true')).lower() != 'false'
1220

13-
@staticmethod
14-
def sdk_enabled():
21+
@classmethod
22+
def sdk_enabled(cls):
1523
"""
1624
Returns whether the SDK is enabled or not.
1725
"""
18-
return SDKConfig.__SDK_ENABLED
26+
return cls.__SDK_ENABLED
1927

20-
@staticmethod
21-
def set_sdk_enabled(value):
28+
@classmethod
29+
def set_sdk_enabled(cls, value):
2230
"""
2331
Modifies the enabled flag if the "AWS_XRAY_ENABLED" environment variable is not set,
2432
otherwise, set the enabled flag to be equal to the environment variable. If the
@@ -29,7 +37,16 @@ def set_sdk_enabled(value):
2937
Environment variables AWS_XRAY_ENABLED overrides argument value.
3038
"""
3139
# Environment Variables take precedence over hardcoded configurations.
32-
if SDKConfig.XRAY_ENABLED_KEY in os.environ:
33-
SDKConfig.__SDK_ENABLED = str(os.getenv(SDKConfig.XRAY_ENABLED_KEY, 'true')).lower() != 'false'
40+
if cls.XRAY_ENABLED_KEY in os.environ:
41+
cls.__SDK_ENABLED = str(os.getenv(cls.XRAY_ENABLED_KEY, 'true')).lower() != 'false'
3442
else:
35-
SDKConfig.__SDK_ENABLED = value
43+
if type(value) == bool:
44+
cls.__SDK_ENABLED = value
45+
else:
46+
cls.__SDK_ENABLED = True
47+
raise InvalidParameterTypeException(
48+
"Invalid parameter type passed into set_sdk_enabled(). Defaulting to True..."
49+
)
50+
51+
# Modify all key paths.
52+
aws_xray_sdk.core.xray_recorder.configure(enabled=cls.__SDK_ENABLED)

tests/ext/aiohttp/test_middleware.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
Expects pytest-aiohttp
55
"""
66
import asyncio
7+
import aws_xray_sdk.sdk_config_module
78
from unittest.mock import patch
89

910
from aiohttp import web
@@ -108,7 +109,9 @@ def recorder(loop):
108109
patcher.start()
109110

110111
xray_recorder.clear_trace_entities()
112+
sdk_enabled_state = aws_xray_sdk.global_sdk_config.sdk_enabled()
111113
yield xray_recorder
114+
aws_xray_sdk.global_sdk_config.set_sdk_enabled(sdk_enabled_state)
112115
xray_recorder.clear_trace_entities()
113116
patcher.stop()
114117

@@ -293,7 +296,7 @@ async def test_disabled_sdk(test_client, loop, recorder):
293296
:param loop: Eventloop fixture
294297
:param recorder: X-Ray recorder fixture
295298
"""
296-
recorder.configure(enabled=False)
299+
aws_xray_sdk.global_sdk_config.set_sdk_enabled(False)
297300
client = await test_client(ServerTest.app(loop=loop))
298301

299302
resp = await client.get('/')

tests/ext/django/test_middleware.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import django
2+
import aws_xray_sdk
23
from django.core.urlresolvers import reverse
34
from django.test import TestCase
45

@@ -14,6 +15,7 @@ def setUp(self):
1415
xray_recorder.configure(context=Context(),
1516
context_missing='LOG_ERROR')
1617
xray_recorder.clear_trace_entities()
18+
aws_xray_sdk.global_sdk_config.set_sdk_enabled(True)
1719

1820
def tearDown(self):
1921
xray_recorder.clear_trace_entities()
@@ -104,7 +106,7 @@ def test_response_header(self):
104106
assert segment.trace_id in trace_header
105107

106108
def test_disabled_sdk(self):
107-
xray_recorder.configure(enabled=False)
109+
aws_xray_sdk.global_sdk_config.set_sdk_enabled(False)
108110
url = reverse('200ok')
109111
self.client.get(url)
110112
segment = xray_recorder.emitter.pop()

tests/test_lambda_context.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import os
22

3-
from aws_xray_sdk.sdk_config import SDKConfig
3+
import aws_xray_sdk
44
from aws_xray_sdk.core import lambda_launcher
55
from aws_xray_sdk.core.models.subsegment import Subsegment
66
from aws_xray_sdk.core.models.dummy_entities import DummySubsegment
@@ -12,6 +12,7 @@
1212

1313
os.environ[lambda_launcher.LAMBDA_TRACE_HEADER_KEY] = HEADER_VAR
1414
context = lambda_launcher.LambdaContext()
15+
aws_xray_sdk.global_sdk_config.set_sdk_enabled(True)
1516

1617

1718
def test_facade_segment_generation():
@@ -46,8 +47,11 @@ def test_put_subsegment():
4647

4748

4849
def test_disable():
49-
SDKConfig.set_sdk_enabled(False)
50+
context.clear_trace_entities()
5051
segment = context.get_trace_entity()
51-
subsegment = Subsegment('name', 'local', segment)
52-
context.put_subsegment(subsegment)
53-
assert type(context.get_trace_entity()) is DummySubsegment
52+
assert segment.sampled
53+
54+
context.clear_trace_entities()
55+
aws_xray_sdk.global_sdk_config.set_sdk_enabled(False)
56+
segment = context.get_trace_entity()
57+
assert not segment.sampled

tests/test_patcher.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
# Python versions < 3 have reload built-in
1414
pass
1515

16-
from aws_xray_sdk.sdk_config import SDKConfig
16+
import aws_xray_sdk
1717
from aws_xray_sdk.core import patcher, xray_recorder
1818
from aws_xray_sdk.core.context import Context
1919

@@ -35,12 +35,14 @@ def construct_ctx():
3535
"""
3636
pre_run_modules = set(module for module in sys.modules.keys())
3737

38+
aws_xray_sdk.global_sdk_config.set_sdk_enabled(True)
3839
xray_recorder.configure(service='test', sampling=False, context=Context())
3940
xray_recorder.clear_trace_entities()
4041
xray_recorder.begin_segment('name')
4142
yield
4243
xray_recorder.end_segment()
4344
xray_recorder.clear_trace_entities()
45+
aws_xray_sdk.global_sdk_config.set_sdk_enabled(True)
4446

4547
# Reload wrapt.importer references to modules to start off clean
4648
reload(wrapt)
@@ -176,7 +178,7 @@ def test_external_submodules_ignores_module():
176178

177179

178180
def test_disable_sdk_disables_patching():
179-
SDKConfig.set_sdk_enabled(False)
181+
aws_xray_sdk.global_sdk_config.set_sdk_enabled(False)
180182
patcher.patch(['tests.mock_module'])
181183
imported_modules = [module for module in TEST_MODULES if module in sys.modules]
182184
assert not imported_modules

tests/test_recorder.py

Lines changed: 2 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,13 @@
55
from aws_xray_sdk.version import VERSION
66
from .util import get_new_stubbed_recorder
77

8-
import os
9-
from aws_xray_sdk.sdk_config import SDKConfig
8+
import aws_xray_sdk
109
from aws_xray_sdk.core.models.segment import Segment
1110
from aws_xray_sdk.core.models.subsegment import Subsegment
1211
from aws_xray_sdk.core.models.dummy_entities import DummySegment, DummySubsegment
1312

1413
xray_recorder = get_new_stubbed_recorder()
15-
XRAY_ENABLED_KEY = SDKConfig.XRAY_ENABLED_KEY
14+
XRAY_ENABLED_KEY = aws_xray_sdk.global_sdk_config.XRAY_ENABLED_KEY
1615

1716

1817
@pytest.fixture(autouse=True)
@@ -188,30 +187,3 @@ def test_disable_is_dummy():
188187
subsegment = xray_recorder.begin_subsegment('name')
189188
assert type(xray_recorder.current_segment()) is DummySegment
190189
assert type(xray_recorder.current_subsegment()) is DummySubsegment
191-
192-
193-
def test_disable_env_precedence():
194-
os.environ[XRAY_ENABLED_KEY] = "False"
195-
xray_recorder.configure(enabled=True)
196-
segment = xray_recorder.begin_segment('name')
197-
subsegment = xray_recorder.begin_subsegment('name')
198-
assert type(xray_recorder.current_segment()) is DummySegment
199-
assert type(xray_recorder.current_subsegment()) is DummySubsegment
200-
201-
202-
def test_disable_env():
203-
os.environ[XRAY_ENABLED_KEY] = "False"
204-
xray_recorder.configure(enabled=False)
205-
segment = xray_recorder.begin_segment('name')
206-
subsegment = xray_recorder.begin_subsegment('name')
207-
assert type(xray_recorder.current_segment()) is DummySegment
208-
assert type(xray_recorder.current_subsegment()) is DummySubsegment
209-
210-
211-
def test_enable_env():
212-
os.environ[XRAY_ENABLED_KEY] = "True"
213-
xray_recorder.configure(enabled=True)
214-
segment = xray_recorder.begin_segment('name')
215-
subsegment = xray_recorder.begin_subsegment('name')
216-
assert type(xray_recorder.current_segment()) is Segment
217-
assert type(xray_recorder.current_subsegment()) is Subsegment

0 commit comments

Comments
 (0)