Skip to content

Commit e55bebb

Browse files
committed
chore: introduce APM_TRACING RC product
We introduce the APM_TRACING remote configuration product that allows dispatching remote configuration to the library for remote enablement/ configuration of library components and features.
1 parent a87b4f7 commit e55bebb

File tree

7 files changed

+108
-9
lines changed

7 files changed

+108
-9
lines changed

ddtrace/debugging/_debugger.py

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -275,8 +275,6 @@ def enable(cls) -> None:
275275

276276
di_config.enabled = True
277277

278-
cls.__watchdog__.install()
279-
280278
if di_config.metrics:
281279
metrics.enable()
282280

@@ -308,7 +306,6 @@ def disable(cls, join: bool = True) -> None:
308306
cls._instance.stop(join=join)
309307
cls._instance = None
310308

311-
cls.__watchdog__.uninstall()
312309
if di_config.metrics:
313310
metrics.disable()
314311

ddtrace/debugging/_products/dynamic_instrumentation.py

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,28 @@
1+
from ddtrace.internal.core.event_hub import on
12
from ddtrace.settings.dynamic_instrumentation import config
23

34

45
requires = ["remote-configuration"]
56

67

78
def post_preload():
8-
pass
9+
from ddtrace.debugging._debugger import Debugger
10+
11+
# We need to install this on start-up because if DI gets enabled remotely
12+
# we won't be able to capture many of the code objects from the modules
13+
# that are already loaded.
14+
Debugger.__watchdog__.install()
15+
16+
17+
def _start():
18+
from ddtrace.debugging import DynamicInstrumentation
19+
20+
DynamicInstrumentation.enable()
921

1022

1123
def start():
1224
if config.enabled:
13-
from ddtrace.debugging import DynamicInstrumentation
14-
15-
DynamicInstrumentation.enable()
25+
_start()
1626

1727

1828
def restart(join=False):
@@ -29,3 +39,21 @@ def stop(join=False):
2939

3040
def at_exit(join=False):
3141
stop(join=join)
42+
43+
44+
def apm_tracing_rc(lib_config):
45+
enabled = lib_config.get("dynamic_instrumentation_enabled")
46+
try:
47+
if enabled is not None: # and config.spec.enabled.full_name not in config.source:
48+
if (config.spec.enabled.full_name not in config.source or config.enabled) and enabled:
49+
_start()
50+
else:
51+
stop()
52+
except Exception:
53+
from traceback import print_exc
54+
55+
print_exc()
56+
raise
57+
58+
59+
on("apm-tracing.rc", apm_tracing_rc, "dynamic-instrumentation")

ddtrace/internal/remoteconfig/products/__init__.py

Whitespace-only changes.
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
from ddtrace import config
2+
from ddtrace.internal.core.event_hub import dispatch
3+
from ddtrace.internal.logger import get_logger
4+
from ddtrace.internal.remoteconfig._connectors import PublisherSubscriberConnector
5+
from ddtrace.internal.remoteconfig._publishers import RemoteConfigPublisher
6+
from ddtrace.internal.remoteconfig._pubsub import PubSub
7+
from ddtrace.internal.remoteconfig._subscribers import RemoteConfigSubscriber
8+
9+
10+
requires = ["remote-configuration"]
11+
12+
13+
log = get_logger(__name__)
14+
15+
16+
def _rc_callback(data, test_tracer=None):
17+
for metadata, data in zip(data["metadata"], data["config"]):
18+
if metadata is None or not isinstance(data, dict):
19+
continue
20+
21+
service_target = data.get("service_target")
22+
if service_target is not None:
23+
service = service_target.get("service")
24+
if service is not None and service != config.service:
25+
continue
26+
27+
env = service_target.get("env")
28+
if env is not None and env != config.env:
29+
continue
30+
31+
lib_config = data.get("lib_config")
32+
if lib_config is not None:
33+
dispatch("apm-tracing.rc", (lib_config,))
34+
35+
36+
class APMTracingAdapter(PubSub):
37+
__publisher_class__ = RemoteConfigPublisher
38+
__subscriber_class__ = RemoteConfigSubscriber
39+
__shared_data__ = PublisherSubscriberConnector()
40+
41+
def __init__(self):
42+
self._publisher = self.__publisher_class__(self.__shared_data__)
43+
self._subscriber = self.__subscriber_class__(self.__shared_data__, _rc_callback, "APM_TRACING")
44+
45+
46+
def post_preload():
47+
pass
48+
49+
50+
def start():
51+
if config._remote_config_enabled:
52+
from ddtrace.internal.remoteconfig.worker import remoteconfig_poller
53+
54+
remoteconfig_poller.register("APM_TRACING", APMTracingAdapter(), restart_on_fork=True)
55+
56+
57+
def restart(join=False):
58+
pass
59+
60+
61+
def stop(join=False):
62+
if config._remote_config_enabled:
63+
from ddtrace.internal.remoteconfig.worker import remoteconfig_poller
64+
65+
remoteconfig_poller.unregister("APM_TRACING")
66+
67+
68+
def at_exit(join=False):
69+
stop(join=join)

pyproject.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,12 @@ ddtrace = "ddtrace.contrib.internal.pytest.plugin"
5959
"ddtrace.pytest_benchmark" = "ddtrace.contrib.internal.pytest_benchmark.plugin"
6060

6161
[project.entry-points.'ddtrace.products']
62+
"apm-tracing-rc" = "ddtrace.internal.remoteconfig.products.apm_tracing"
6263
"code-origin-for-spans" = "ddtrace.debugging._products.code_origin.span"
6364
"dynamic-instrumentation" = "ddtrace.debugging._products.dynamic_instrumentation"
6465
"exception-replay" = "ddtrace.debugging._products.exception_replay"
6566
"live-debugger" = "ddtrace.debugging._products.live_debugger"
66-
"remote-configuration" = "ddtrace.internal.remoteconfig.product"
67+
"remote-configuration" = "ddtrace.internal.remoteconfig.products.client"
6768
"symbol-database" = "ddtrace.internal.symbol_db.product"
6869
"appsec" = "ddtrace.internal.appsec.product"
6970
"iast" = "ddtrace.internal.iast.product"

tests/debugging/mocking.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -203,7 +203,11 @@ def _debugger(config_to_override: En, config_overrides: Any) -> Generator[TestDe
203203
def debugger(**config_overrides: Any) -> Generator[TestDebugger, None, None]:
204204
"""Test with the debugger enabled."""
205205
with _debugger(di_config, config_overrides) as debugger:
206-
yield debugger
206+
debugger.__watchdog__.install()
207+
try:
208+
yield debugger
209+
finally:
210+
debugger.__watchdog__.uninstall()
207211

208212

209213
class MockSpanExceptionHandler(SpanExceptionHandler):

0 commit comments

Comments
 (0)