Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 8 additions & 5 deletions ddtrace/_trace/product.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,17 @@ def post_preload():
_patch_all(**modules_to_bool)


def enabled():
return _config.enabled


def start():
if _config.enabled:
from ddtrace.internal.settings._config import config
from ddtrace.internal.settings._config import config

if config._trace_methods:
from ddtrace.internal.tracemethods import _install_trace_methods
if config._trace_methods:
from ddtrace.internal.tracemethods import _install_trace_methods

_install_trace_methods(config._trace_methods)
_install_trace_methods(config._trace_methods)


def restart(join=False):
Expand Down
36 changes: 14 additions & 22 deletions ddtrace/debugging/_products/code_origin/span.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,50 +20,42 @@
# requires = ["tracer"]


def post_preload() -> None:
pass
# We need to instrument the entrypoints on boot because this is the only
# time the tracer will notify us of entrypoints being registered.
@partial(core.on, "service_entrypoint.patch")
def _(f: t.Union[FunctionType, MethodType]) -> None:
from ddtrace.debugging._origin.span import SpanCodeOriginProcessorEntry

SpanCodeOriginProcessorEntry.instrument_view(f)

def _start() -> None:
from ddtrace.debugging._origin.span import SpanCodeOriginProcessorEntry

SpanCodeOriginProcessorEntry.enable()
def post_preload() -> None:
pass


def start() -> None:
# We need to instrument the entrypoints on boot because this is the only
# time the tracer will notify us of entrypoints being registered.
@partial(core.on, "service_entrypoint.patch")
def _(f: t.Union[FunctionType, MethodType]) -> None:
from ddtrace.debugging._origin.span import SpanCodeOriginProcessorEntry
from ddtrace.debugging._origin.span import SpanCodeOriginProcessorEntry

SpanCodeOriginProcessorEntry.instrument_view(f)
SpanCodeOriginProcessorEntry.enable()

log.debug("Registered entrypoint patching hook for code origin for spans")

def enabled() -> bool:
# If dynamic instrumentation is enabled, and code origin for spans is not explicitly disabled,
# we'll enable code origin for spans.
di_enabled = product_manager.is_enabled(DI_PRODUCT_KEY) and config.value_source(CO_ENABLED) == ValueSource.DEFAULT
if config.span.enabled or di_enabled:
_start()
return config.span.enabled or di_enabled


def restart(join: bool = False) -> None:
pass


def _stop() -> None:
def stop(join: bool = False) -> None:
from ddtrace.debugging._origin.span import SpanCodeOriginProcessorEntry

SpanCodeOriginProcessorEntry.disable()


def stop(join: bool = False) -> None:
di_enabled = product_manager.is_enabled(DI_PRODUCT_KEY) and config.value_source(CO_ENABLED) == ValueSource.DEFAULT
if config.span.enabled or di_enabled:
_stop()


def at_exit(join: bool = False) -> None:
stop(join=join)

Expand All @@ -75,4 +67,4 @@ class APMCapabilities(enum.IntFlag):
def apm_tracing_rc(lib_config: t.Any, _config: t.Any) -> None:
if (enabled := lib_config.get("code_origin_enabled")) is not None:
should_start = (config.span.spec.enabled.full_name not in config.source or config.span.enabled) and enabled
_start() if should_start else _stop()
start() if should_start else stop()
27 changes: 12 additions & 15 deletions ddtrace/debugging/_products/dynamic_instrumentation.py
Original file line number Diff line number Diff line change
@@ -1,32 +1,30 @@
import enum
from typing import Any

from ddtrace.debugging._import import DebuggerModuleWatchdog
from ddtrace.internal.settings.dynamic_instrumentation import config


# 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.
DebuggerModuleWatchdog.install()

requires = ["remote-configuration"]


def post_preload() -> None:
pass


def _start() -> None:
def start() -> None:
from ddtrace.debugging import DynamicInstrumentation

DynamicInstrumentation.enable()


def start() -> None:
from ddtrace.debugging._import import DebuggerModuleWatchdog

# 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.
DebuggerModuleWatchdog.install()

if config.enabled:
_start()
def enabled() -> bool:
return config.enabled


def before_fork() -> None:
Expand All @@ -40,10 +38,9 @@ def restart(join: bool = False) -> None:


def stop(join: bool = False) -> None:
if config.enabled:
from ddtrace.debugging import DynamicInstrumentation
from ddtrace.debugging import DynamicInstrumentation

DynamicInstrumentation.disable(join=join)
DynamicInstrumentation.disable(join=join)


def at_exit(join: bool = False) -> None:
Expand All @@ -57,4 +54,4 @@ class APMCapabilities(enum.IntFlag):
def apm_tracing_rc(lib_config: Any, _config: Any) -> None:
if (enabled := lib_config.get("dynamic_instrumentation_enabled")) is not None:
should_start = (config.spec.enabled.full_name not in config.source or config.enabled) and enabled
_start() if should_start else stop()
start() if should_start else stop()
16 changes: 5 additions & 11 deletions ddtrace/debugging/_products/exception_replay.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,32 +12,26 @@ def post_preload() -> None:
pass


def _start() -> None:
def start() -> None:
from ddtrace.debugging._exception.replay import SpanExceptionHandler

SpanExceptionHandler.enable()


def start() -> None:
if config.enabled:
_start()
def enabled() -> bool:
return config.enabled


def restart(join: bool = False) -> None:
pass


def _stop() -> None:
def stop(join: bool = False) -> None:
from ddtrace.debugging._exception.replay import SpanExceptionHandler

SpanExceptionHandler.disable()


def stop(join: bool = False) -> None:
if config.enabled:
_stop()


def at_exit(join: bool = False) -> None:
stop(join=join)

Expand All @@ -49,4 +43,4 @@ class APMCapabilities(enum.IntFlag):
def apm_tracing_rc(lib_config: Any, _config: Any) -> None:
if (enabled := lib_config.get("exception_replay_enabled")) is not None:
should_start = (config.spec.enabled.full_name not in config.source or config.enabled) and enabled
_start() if should_start else _stop()
start() if should_start else stop()
14 changes: 8 additions & 6 deletions ddtrace/debugging/_products/live_debugger.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,24 @@ def post_preload() -> None:
pass


def enabled() -> bool:
return config.enabled


def start() -> None:
if config.enabled:
from ddtrace.debugging._live import enable
from ddtrace.debugging._live import enable

enable()
enable()


def restart(join: bool = False) -> None:
pass


def stop(join: bool = False) -> None:
if config.enabled:
from ddtrace.debugging._live import disable
from ddtrace.debugging._live import disable

disable()
disable()


def at_exit(join: bool = False) -> None:
Expand Down
14 changes: 8 additions & 6 deletions ddtrace/errortracking/product.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,24 @@ def post_preload():
pass


def enabled() -> bool:
return config.enabled


def start() -> None:
if config.enabled:
from ddtrace.errortracking._handled_exceptions.collector import HandledExceptionCollector
from ddtrace.errortracking._handled_exceptions.collector import HandledExceptionCollector

HandledExceptionCollector.enable()
HandledExceptionCollector.enable()


def restart(join: bool = False) -> None:
pass


def stop(join: bool = False):
if config.enabled:
from ddtrace.errortracking._handled_exceptions.collector import HandledExceptionCollector
from ddtrace.errortracking._handled_exceptions.collector import HandledExceptionCollector

HandledExceptionCollector.disable()
HandledExceptionCollector.disable()


def at_exit(join: bool = False):
Expand Down
5 changes: 3 additions & 2 deletions ddtrace/internal/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,12 @@ object that implements the product protocol. This consists of a Python object

| Attribute | Description |
|-----------|-------------|
| `start() -> None` | A function with the logic required to start the product |
| `post_preload() -> None` | A function with the logic required to finish initialization after the library preload stage |
| `enabled() -> bool` | A function that returns whether the product should be started; called before `start()` by the product manager |
| `start() -> None` | A function with the logic required to enable the product (called only when `enabled()` returns `True`) |
| `restart(join: bool = False) -> None` | A function with the logic required to restart the product after a fork |
| `stop(join: bool = False) -> None` | A function with the logic required to stop the product |
| `at_exit(join: bool = False) -> None` | A function with the logic required to stop the product at exit |
| `post_preload() -> None` | A function with the logic required to finish initialization after the library preload stage |

The product object needs to be made available to the Python plugin system by
defining an entry point in the `project.entry-points.'ddtrace.products'` section
Expand Down
6 changes: 6 additions & 0 deletions ddtrace/internal/appsec/product.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,12 @@ def post_preload():
pass


def enabled():
return (
config._asm_enabled or config._asm_can_be_enabled or config._asm_rc_enabled or ai_guard_config._ai_guard_enabled
)


def start():
if config._asm_enabled or config._asm_can_be_enabled:
from ddtrace.appsec._listeners import load_common_appsec_modules
Expand Down
12 changes: 9 additions & 3 deletions ddtrace/internal/iast/product.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,14 +91,20 @@ def post_preload():
# Dropping inspect causes: "unexpected object <Signature> in __signature__ attribute"


def enabled():
"""
Return whether the IAST product is enabled.
"""
return asm_config._iast_enabled


def start():
"""
Start the IAST product.
"""
if asm_config._iast_enabled:
from ddtrace.appsec._iast.processor import AppSecIastSpanProcessor
from ddtrace.appsec._iast.processor import AppSecIastSpanProcessor

AppSecIastSpanProcessor.enable()
AppSecIastSpanProcessor.enable()


def restart(join=False):
Expand Down
14 changes: 8 additions & 6 deletions ddtrace/internal/openfeature/product.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,24 @@ def post_preload():
pass


def enabled():
return ffe_config.experimental_flagging_provider_enabled


def start():
if ffe_config.experimental_flagging_provider_enabled:
from ddtrace.internal.openfeature._remoteconfiguration import enable_featureflags_rc
from ddtrace.internal.openfeature._remoteconfiguration import enable_featureflags_rc

enable_featureflags_rc()
enable_featureflags_rc()


def restart(join=False):
pass


def stop(join=False):
if ffe_config.experimental_flagging_provider_enabled:
from ddtrace.internal.openfeature._remoteconfiguration import disable_featureflags_rc
from ddtrace.internal.openfeature._remoteconfiguration import disable_featureflags_rc

disable_featureflags_rc()
disable_featureflags_rc()


def at_exit(join=False):
Expand Down
16 changes: 11 additions & 5 deletions ddtrace/internal/products.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@ class Product(Protocol):

def post_preload(self) -> None: ...

def enabled(self) -> bool: ...

def start(self) -> None: ...

def restart(self, join: bool = False) -> None: ...
Expand Down Expand Up @@ -129,6 +131,9 @@ def start_products(self) -> None:
continue

try:
if not product.enabled():
log.debug("Product '%s' is not enabled, skipping", name)
continue
product.start()
log.debug("Started product '%s'", name)
telemetry_writer.product_activated(name.replace("-", "_"), True)
Expand Down Expand Up @@ -169,6 +174,8 @@ def restart_products(self, join: bool = False) -> None:
def stop_products(self, join: bool = False) -> None:
for name, product in reversed(self.products):
try:
if not product.enabled():
continue
product.stop(join=join)
log.debug("Stopped product '%s'", name)
telemetry_writer.product_activated(name.replace("-", "_"), False)
Expand All @@ -178,6 +185,8 @@ def stop_products(self, join: bool = False) -> None:
def exit_products(self, join: bool = False) -> None:
for name, product in reversed(self.products):
try:
if not product.enabled():
continue
log.debug("Exiting product '%s'", name)
product.at_exit(join=join)
except Exception:
Expand Down Expand Up @@ -232,14 +241,11 @@ def _() -> None:
else:
self._do_products()

def is_enabled(self, product_name: str, enabled_attribute: str = "enabled") -> bool:
def is_enabled(self, product_name: str) -> bool:
if (product := self.__products__.get(product_name)) is None:
return False

if (config := getattr(product, "config", None)) is None:
return False

return getattr(config, enabled_attribute, False)
return product.enabled()


manager = ProductManager()
Loading