Skip to content
Merged
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
17 changes: 10 additions & 7 deletions ddtrace/bootstrap/sitecustomize.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,21 +13,21 @@
# to allow injecting a custom sitecustomize.py file into the Python process to
# perform the correct initialisation for the library. All the actual
# initialisation logic should be placed in preload.py.
from ddtrace import LOADED_MODULES # isort:skip

import ddtrace # isort:skip
import logging # noqa:I001
import os # noqa:F401
import sys
import warnings # noqa:F401

from ddtrace.internal.telemetry import telemetry_writer
from ddtrace import config # noqa:F401
from ddtrace._logger import DD_LOG_FORMAT
from ddtrace.internal.logger import get_logger # noqa:F401
from ddtrace.internal.module import ModuleWatchdog # noqa:F401
from ddtrace.internal.module import is_module_installed
from ddtrace.internal.telemetry import telemetry_writer
from ddtrace.internal.utils.formats import asbool # noqa:F401


# Debug mode from the tracer will do the same here, so only need to do this otherwise.
if config._logs_injection:
from ddtrace import patch
Expand Down Expand Up @@ -88,7 +88,7 @@ def drop(module_name):
"wrapt",
]
)
for m in list(_ for _ in sys.modules if _ not in LOADED_MODULES):
for m in list(_ for _ in sys.modules if _ not in ddtrace.LOADED_MODULES):
if any(m == _ or m.startswith(_ + ".") for _ in KEEP_MODULES):
continue

Expand Down Expand Up @@ -164,9 +164,12 @@ def _(threading):
log.debug("additional sitecustomize not found")
else:
log.debug("additional sitecustomize found in: %s", sys.path)

telemetry_writer.add_configuration("ddtrace_bootstrapped", True, "unknown")
telemetry_writer.add_configuration("ddtrace_auto_used", "ddtrace.auto" in sys.modules, "unknown")
# Detect if ddtrace-run is being used by checking if the bootstrap directory is in the python path
bootstrap_dir = os.path.join(os.path.dirname(ddtrace.__file__), "bootstrap")
if bootstrap_dir in sys.path:
telemetry_writer.add_configuration("instrumentation_source", "cmd_line", "code")
else:
telemetry_writer.add_configuration("instrumentation_source", "manual", "code")
# Loading status used in tests to detect if the `sitecustomize` has been
# properly loaded without exceptions. This must be the last action in the module
# when the execution ends with a success.
Expand Down
10 changes: 4 additions & 6 deletions ddtrace/settings/_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@

from ddtrace.internal.serverless import in_azure_function
from ddtrace.internal.serverless import in_gcp_function
from ddtrace.internal.telemetry import telemetry_writer
from ddtrace.internal.telemetry import validate_otel_envs
from ddtrace.internal.utils.cache import cachedmethod

Expand Down Expand Up @@ -647,9 +648,9 @@ def __init__(self):
self._llmobs_ml_app = _get_config("DD_LLMOBS_ML_APP")
self._llmobs_agentless_enabled = _get_config("DD_LLMOBS_AGENTLESS_ENABLED", None, asbool)

self._inject_force = _get_config("DD_INJECT_FORCE", False, asbool)
self._inject_force = _get_config("DD_INJECT_FORCE", None, asbool)
self._lib_was_injected = False
self._inject_was_attempted = _get_config("_DD_INJECT_WAS_ATTEMPTED", False, asbool)
self._inject_enabled = _get_config("DD_INJECTION_ENABLED")
self._inferred_proxy_services_enabled = _get_config("DD_TRACE_INFERRED_PROXY_SERVICES_ENABLED", False, asbool)

def __getattr__(self, name) -> Any:
Expand Down Expand Up @@ -778,10 +779,7 @@ def _set_config_items(self, items):
item_names.append(key)
item = self._config[key]
item.set_value_source(value, origin)
if self._telemetry_enabled:
from ddtrace.internal.telemetry import telemetry_writer

telemetry_writer.add_configuration(item._name, item.value(), item.source())
telemetry_writer.add_configuration(item._name, item.value(), item.source())
self._notify_subscribers(item_names)

def _reset(self):
Expand Down
4 changes: 3 additions & 1 deletion lib-injection/sources/sitecustomize.py
Original file line number Diff line number Diff line change
Expand Up @@ -262,7 +262,6 @@ def _inject():
EXECUTABLES_DENY_LIST = build_denied_executables()
integration_incomp = False
runtime_incomp = False
os.environ["_DD_INJECT_WAS_ATTEMPTED"] = "true"
spec = None
try:
# `find_spec` is only available in Python 3.4+
Expand Down Expand Up @@ -430,6 +429,9 @@ def _inject():
],
),
)
# Track whether library injection was successful
ddtrace.config._lib_was_injected = True
ddtrace.internal.telemetry.telemetry_writer.add_configuration("instrumentation_source", "ssi", "code")
except Exception as e:
TELEMETRY_DATA.append(
create_count_metric(
Expand Down
35 changes: 30 additions & 5 deletions tests/telemetry/test_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from ddtrace.internal.utils.version import _pep440_to_semver
from ddtrace.settings._config import DD_TRACE_OBFUSCATION_QUERY_STRING_REGEXP_DEFAULT
from tests.conftest import DEFAULT_DDTRACE_SUBPROCESS_TEST_SERVICE_NAME
from tests.utils import call_program
from tests.utils import override_global_config


Expand Down Expand Up @@ -159,8 +160,7 @@ def test_app_started_event(telemetry_writer, test_agent_session, mock_time):
{"name": "DD_TRACE_WRITER_INTERVAL_SECONDS", "origin": "unknown", "value": 1.0},
{"name": "DD_TRACE_WRITER_MAX_PAYLOAD_SIZE_BYTES", "origin": "unknown", "value": 20 << 20},
{"name": "DD_TRACE_WRITER_REUSE_CONNECTIONS", "origin": "unknown", "value": False},
{"name": "ddtrace_auto_used", "origin": "unknown", "value": False},
{"name": "ddtrace_bootstrapped", "origin": "unknown", "value": False},
{"name": "instrumentation_source", "origin": "code", "value": "manual"},
{"name": "profiling_enabled", "origin": "default", "value": "false"},
{"name": "data_streams_enabled", "origin": "default", "value": "false"},
{"name": "appsec_enabled", "origin": "default", "value": "false"},
Expand Down Expand Up @@ -283,6 +283,8 @@ def test_app_started_event_configuration_override(test_agent_session, run_python
env["DD_API_SECURITY_ENABLED"] = "False"
env["DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING_ENABLED"] = "False"
env["DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE"] = "disabled"
env["DD_INJECT_FORCE"] = "true"
env["DD_INJECTION_ENABLED"] = "tracer"

# By default telemetry collection is enabled after 10 seconds, so we either need to
# to sleep for 10 seconds or manually call _app_started() to generate the app started event.
Expand Down Expand Up @@ -403,6 +405,7 @@ def test_app_started_event_configuration_override(test_agent_session, run_python
{"name": "DD_IAST_STACK_TRACE_ENABLED", "origin": "default", "value": True},
{"name": "DD_IAST_TELEMETRY_VERBOSITY", "origin": "default", "value": "INFORMATION"},
{"name": "DD_IAST_VULNERABILITIES_PER_REQUEST", "origin": "default", "value": 2},
{"name": "DD_INJECTION_ENABLED", "origin": "env_var", "value": "tracer"},
{"name": "DD_INJECT_FORCE", "origin": "env_var", "value": True},
{"name": "DD_INSTRUMENTATION_INSTALL_ID", "origin": "default", "value": None},
{"name": "DD_INSTRUMENTATION_INSTALL_TYPE", "origin": "default", "value": None},
Expand Down Expand Up @@ -524,10 +527,8 @@ def test_app_started_event_configuration_override(test_agent_session, run_python
{"name": "DD_VERSION", "origin": "default", "value": None},
{"name": "_DD_APPSEC_DEDUPLICATION_ENABLED", "origin": "default", "value": True},
{"name": "_DD_IAST_LAZY_TAINT", "origin": "default", "value": False},
{"name": "_DD_INJECT_WAS_ATTEMPTED", "origin": "default", "value": False},
{"name": "_DD_TRACE_WRITER_LOG_ERROR_PAYLOADS", "origin": "default", "value": False},
{"name": "ddtrace_auto_used", "origin": "unknown", "value": True},
{"name": "ddtrace_bootstrapped", "origin": "unknown", "value": True},
{"name": "instrumentation_source", "origin": "code", "value": "manual"},
{"name": "python_build_gnu_type", "origin": "unknown", "value": sysconfig.get_config_var("BUILD_GNU_TYPE")},
{"name": "python_host_gnu_type", "origin": "unknown", "value": sysconfig.get_config_var("HOST_GNU_TYPE")},
{"name": "python_soabi", "origin": "unknown", "value": sysconfig.get_config_var("SOABI")},
Expand All @@ -549,6 +550,30 @@ def test_update_dependencies_event(test_agent_session, ddtrace_run_python_code_i
assert len(deps) == 1, deps


def test_instrumentation_source_config(
test_agent_session, ddtrace_run_python_code_in_subprocess, run_python_code_in_subprocess
):
env = os.environ.copy()
env["_DD_INSTRUMENTATION_TELEMETRY_TESTS_FORCE_APP_STARTED"] = "true"

_, stderr, status, _ = call_program("ddtrace-run", sys.executable, "-c", "", env=env)
assert status == 0, stderr
configs = test_agent_session.get_configurations("instrumentation_source")
assert configs and configs[-1]["value"] == "cmd_line"
test_agent_session.clear()

_, stderr, status, _ = call_program(sys.executable, "-c", "import ddtrace.auto", env=env)
assert status == 0, stderr
configs = test_agent_session.get_configurations("instrumentation_source")
assert configs and configs[-1]["value"] == "manual"
test_agent_session.clear()

_, stderr, status, _ = call_program(sys.executable, "-c", "import ddtrace", env=env)
assert status == 0, stderr
configs = test_agent_session.get_configurations("instrumentation_source")
assert not configs, "instrumentation_source should not be set when ddtrace instrumentation is not used"


def test_update_dependencies_event_when_disabled(test_agent_session, ddtrace_run_python_code_in_subprocess):
env = os.environ.copy()
# app-started events are sent 10 seconds after ddtrace imported, this configuration overrides this
Expand Down
Loading