Skip to content

Commit

Permalink
chore: introduce APM_TRACING RC product
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
P403n1x87 committed Feb 14, 2025
1 parent a87b4f7 commit e55bebb
Show file tree
Hide file tree
Showing 7 changed files with 108 additions and 9 deletions.
3 changes: 0 additions & 3 deletions ddtrace/debugging/_debugger.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,8 +275,6 @@ def enable(cls) -> None:

di_config.enabled = True

cls.__watchdog__.install()

if di_config.metrics:
metrics.enable()

Expand Down Expand Up @@ -308,7 +306,6 @@ def disable(cls, join: bool = True) -> None:
cls._instance.stop(join=join)
cls._instance = None

cls.__watchdog__.uninstall()
if di_config.metrics:
metrics.disable()

Expand Down
36 changes: 32 additions & 4 deletions ddtrace/debugging/_products/dynamic_instrumentation.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,28 @@
from ddtrace.internal.core.event_hub import on
from ddtrace.settings.dynamic_instrumentation import config


requires = ["remote-configuration"]


def post_preload():
pass
from ddtrace.debugging._debugger import Debugger

# We need to install this on start-up because if DI gets enabled remotely
# we won't be able to capture many of the code objects from the modules
# that are already loaded.
Debugger.__watchdog__.install()


def _start():
from ddtrace.debugging import DynamicInstrumentation

DynamicInstrumentation.enable()


def start():
if config.enabled:
from ddtrace.debugging import DynamicInstrumentation

DynamicInstrumentation.enable()
_start()


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

def at_exit(join=False):
stop(join=join)


def apm_tracing_rc(lib_config):
enabled = lib_config.get("dynamic_instrumentation_enabled")
try:
if enabled is not None: # and config.spec.enabled.full_name not in config.source:
if (config.spec.enabled.full_name not in config.source or config.enabled) and enabled:
_start()
else:
stop()
except Exception:
from traceback import print_exc

print_exc()
raise


on("apm-tracing.rc", apm_tracing_rc, "dynamic-instrumentation")
Empty file.
69 changes: 69 additions & 0 deletions ddtrace/internal/remoteconfig/products/apm_tracing.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from ddtrace import config
from ddtrace.internal.core.event_hub import dispatch
from ddtrace.internal.logger import get_logger
from ddtrace.internal.remoteconfig._connectors import PublisherSubscriberConnector
from ddtrace.internal.remoteconfig._publishers import RemoteConfigPublisher
from ddtrace.internal.remoteconfig._pubsub import PubSub
from ddtrace.internal.remoteconfig._subscribers import RemoteConfigSubscriber


requires = ["remote-configuration"]


log = get_logger(__name__)


def _rc_callback(data, test_tracer=None):
for metadata, data in zip(data["metadata"], data["config"]):
if metadata is None or not isinstance(data, dict):
continue

service_target = data.get("service_target")
if service_target is not None:
service = service_target.get("service")
if service is not None and service != config.service:
continue

env = service_target.get("env")
if env is not None and env != config.env:
continue

lib_config = data.get("lib_config")
if lib_config is not None:
dispatch("apm-tracing.rc", (lib_config,))


class APMTracingAdapter(PubSub):
__publisher_class__ = RemoteConfigPublisher
__subscriber_class__ = RemoteConfigSubscriber
__shared_data__ = PublisherSubscriberConnector()

def __init__(self):
self._publisher = self.__publisher_class__(self.__shared_data__)
self._subscriber = self.__subscriber_class__(self.__shared_data__, _rc_callback, "APM_TRACING")


def post_preload():
pass


def start():
if config._remote_config_enabled:
from ddtrace.internal.remoteconfig.worker import remoteconfig_poller

remoteconfig_poller.register("APM_TRACING", APMTracingAdapter(), restart_on_fork=True)


def restart(join=False):
pass


def stop(join=False):
if config._remote_config_enabled:
from ddtrace.internal.remoteconfig.worker import remoteconfig_poller

remoteconfig_poller.unregister("APM_TRACING")


def at_exit(join=False):
stop(join=join)
File renamed without changes.
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,12 @@ ddtrace = "ddtrace.contrib.internal.pytest.plugin"
"ddtrace.pytest_benchmark" = "ddtrace.contrib.internal.pytest_benchmark.plugin"

[project.entry-points.'ddtrace.products']
"apm-tracing-rc" = "ddtrace.internal.remoteconfig.products.apm_tracing"
"code-origin-for-spans" = "ddtrace.debugging._products.code_origin.span"
"dynamic-instrumentation" = "ddtrace.debugging._products.dynamic_instrumentation"
"exception-replay" = "ddtrace.debugging._products.exception_replay"
"live-debugger" = "ddtrace.debugging._products.live_debugger"
"remote-configuration" = "ddtrace.internal.remoteconfig.product"
"remote-configuration" = "ddtrace.internal.remoteconfig.products.client"
"symbol-database" = "ddtrace.internal.symbol_db.product"
"appsec" = "ddtrace.internal.appsec.product"
"iast" = "ddtrace.internal.iast.product"
Expand Down
6 changes: 5 additions & 1 deletion tests/debugging/mocking.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,7 +203,11 @@ def _debugger(config_to_override: En, config_overrides: Any) -> Generator[TestDe
def debugger(**config_overrides: Any) -> Generator[TestDebugger, None, None]:
"""Test with the debugger enabled."""
with _debugger(di_config, config_overrides) as debugger:
yield debugger
debugger.__watchdog__.install()
try:
yield debugger
finally:
debugger.__watchdog__.uninstall()


class MockSpanExceptionHandler(SpanExceptionHandler):
Expand Down

0 comments on commit e55bebb

Please sign in to comment.