Skip to content

Commit 85e6e9f

Browse files
authored
chore(ssi): improve telemetry submitted for ssi configurations (#13249)
- Ensure `config._lib_was_injected` is set to `True` when SSI is successful. Currently this configuration is always set to `False` even when SSI is successful. - Use `DD_INJECTION_ENABLED` to track whether SSI was attempted. This env_var is set by the injector and is used by other SDKs. ## Checklist - [ ] PR author has checked that all the criteria below are met - The PR description includes an overview of the change - The PR description articulates the motivation for the change - The change includes tests OR the PR description describes a testing strategy - The PR description notes risks associated with the change, if any - Newly-added code is easy to change - The change follows the [library release note guidelines](https://ddtrace.readthedocs.io/en/stable/releasenotes.html) - The change includes or references documentation updates if necessary - Backport labels are set (if [applicable](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)) ## Reviewer Checklist - [ ] Reviewer has checked that all the criteria below are met - Title is accurate - All changes are related to the pull request's stated goal - Avoids breaking [API](https://ddtrace.readthedocs.io/en/stable/versioning.html#interfaces) changes - Testing strategy adequately addresses listed risks - Newly-added code is easy to change - Release note makes sense to a user of the library - If necessary, author has acknowledged and discussed the performance implications of this PR as reported in the benchmarks PR comment - Backport labels are set in a manner that is consistent with the [release branch maintenance policy](https://ddtrace.readthedocs.io/en/latest/contributing.html#backporting)
1 parent 8fe0cfa commit 85e6e9f

File tree

4 files changed

+47
-19
lines changed

4 files changed

+47
-19
lines changed

ddtrace/bootstrap/sitecustomize.py

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,21 @@
1313
# to allow injecting a custom sitecustomize.py file into the Python process to
1414
# perform the correct initialisation for the library. All the actual
1515
# initialisation logic should be placed in preload.py.
16-
from ddtrace import LOADED_MODULES # isort:skip
17-
16+
import ddtrace # isort:skip
1817
import logging # noqa:I001
1918
import os # noqa:F401
2019
import sys
2120
import warnings # noqa:F401
2221

23-
from ddtrace.internal.telemetry import telemetry_writer
2422
from ddtrace import config # noqa:F401
2523
from ddtrace._logger import DD_LOG_FORMAT
2624
from ddtrace.internal.logger import get_logger # noqa:F401
2725
from ddtrace.internal.module import ModuleWatchdog # noqa:F401
2826
from ddtrace.internal.module import is_module_installed
27+
from ddtrace.internal.telemetry import telemetry_writer
2928
from ddtrace.internal.utils.formats import asbool # noqa:F401
3029

30+
3131
# Debug mode from the tracer will do the same here, so only need to do this otherwise.
3232
if config._logs_injection:
3333
from ddtrace import patch
@@ -88,7 +88,7 @@ def drop(module_name):
8888
"wrapt",
8989
]
9090
)
91-
for m in list(_ for _ in sys.modules if _ not in LOADED_MODULES):
91+
for m in list(_ for _ in sys.modules if _ not in ddtrace.LOADED_MODULES):
9292
if any(m == _ or m.startswith(_ + ".") for _ in KEEP_MODULES):
9393
continue
9494

@@ -164,9 +164,12 @@ def _(threading):
164164
log.debug("additional sitecustomize not found")
165165
else:
166166
log.debug("additional sitecustomize found in: %s", sys.path)
167-
168-
telemetry_writer.add_configuration("ddtrace_bootstrapped", True, "unknown")
169-
telemetry_writer.add_configuration("ddtrace_auto_used", "ddtrace.auto" in sys.modules, "unknown")
167+
# Detect if ddtrace-run is being used by checking if the bootstrap directory is in the python path
168+
bootstrap_dir = os.path.join(os.path.dirname(ddtrace.__file__), "bootstrap")
169+
if bootstrap_dir in sys.path:
170+
telemetry_writer.add_configuration("instrumentation_source", "cmd_line", "code")
171+
else:
172+
telemetry_writer.add_configuration("instrumentation_source", "manual", "code")
170173
# Loading status used in tests to detect if the `sitecustomize` has been
171174
# properly loaded without exceptions. This must be the last action in the module
172175
# when the execution ends with a success.

ddtrace/settings/_config.py

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
from ddtrace.internal.serverless import in_azure_function
1616
from ddtrace.internal.serverless import in_gcp_function
17+
from ddtrace.internal.telemetry import telemetry_writer
1718
from ddtrace.internal.telemetry import validate_otel_envs
1819
from ddtrace.internal.utils.cache import cachedmethod
1920

@@ -647,9 +648,9 @@ def __init__(self):
647648
self._llmobs_ml_app = _get_config("DD_LLMOBS_ML_APP")
648649
self._llmobs_agentless_enabled = _get_config("DD_LLMOBS_AGENTLESS_ENABLED", None, asbool)
649650

650-
self._inject_force = _get_config("DD_INJECT_FORCE", False, asbool)
651+
self._inject_force = _get_config("DD_INJECT_FORCE", None, asbool)
651652
self._lib_was_injected = False
652-
self._inject_was_attempted = _get_config("_DD_INJECT_WAS_ATTEMPTED", False, asbool)
653+
self._inject_enabled = _get_config("DD_INJECTION_ENABLED")
653654
self._inferred_proxy_services_enabled = _get_config("DD_TRACE_INFERRED_PROXY_SERVICES_ENABLED", False, asbool)
654655

655656
def __getattr__(self, name) -> Any:
@@ -778,10 +779,7 @@ def _set_config_items(self, items):
778779
item_names.append(key)
779780
item = self._config[key]
780781
item.set_value_source(value, origin)
781-
if self._telemetry_enabled:
782-
from ddtrace.internal.telemetry import telemetry_writer
783-
784-
telemetry_writer.add_configuration(item._name, item.value(), item.source())
782+
telemetry_writer.add_configuration(item._name, item.value(), item.source())
785783
self._notify_subscribers(item_names)
786784

787785
def _reset(self):

lib-injection/sources/sitecustomize.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -262,7 +262,6 @@ def _inject():
262262
EXECUTABLES_DENY_LIST = build_denied_executables()
263263
integration_incomp = False
264264
runtime_incomp = False
265-
os.environ["_DD_INJECT_WAS_ATTEMPTED"] = "true"
266265
spec = None
267266
try:
268267
# `find_spec` is only available in Python 3.4+
@@ -430,6 +429,9 @@ def _inject():
430429
],
431430
),
432431
)
432+
# Track whether library injection was successful
433+
ddtrace.config._lib_was_injected = True
434+
ddtrace.internal.telemetry.telemetry_writer.add_configuration("instrumentation_source", "ssi", "code")
433435
except Exception as e:
434436
TELEMETRY_DATA.append(
435437
create_count_metric(

tests/telemetry/test_writer.py

Lines changed: 30 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
from ddtrace.internal.utils.version import _pep440_to_semver
1919
from ddtrace.settings._config import DD_TRACE_OBFUSCATION_QUERY_STRING_REGEXP_DEFAULT
2020
from tests.conftest import DEFAULT_DDTRACE_SUBPROCESS_TEST_SERVICE_NAME
21+
from tests.utils import call_program
2122
from tests.utils import override_global_config
2223

2324

@@ -159,8 +160,7 @@ def test_app_started_event(telemetry_writer, test_agent_session, mock_time):
159160
{"name": "DD_TRACE_WRITER_INTERVAL_SECONDS", "origin": "unknown", "value": 1.0},
160161
{"name": "DD_TRACE_WRITER_MAX_PAYLOAD_SIZE_BYTES", "origin": "unknown", "value": 20 << 20},
161162
{"name": "DD_TRACE_WRITER_REUSE_CONNECTIONS", "origin": "unknown", "value": False},
162-
{"name": "ddtrace_auto_used", "origin": "unknown", "value": False},
163-
{"name": "ddtrace_bootstrapped", "origin": "unknown", "value": False},
163+
{"name": "instrumentation_source", "origin": "code", "value": "manual"},
164164
{"name": "profiling_enabled", "origin": "default", "value": "false"},
165165
{"name": "data_streams_enabled", "origin": "default", "value": "false"},
166166
{"name": "appsec_enabled", "origin": "default", "value": "false"},
@@ -283,6 +283,8 @@ def test_app_started_event_configuration_override(test_agent_session, run_python
283283
env["DD_API_SECURITY_ENABLED"] = "False"
284284
env["DD_APPSEC_AUTOMATED_USER_EVENTS_TRACKING_ENABLED"] = "False"
285285
env["DD_APPSEC_AUTO_USER_INSTRUMENTATION_MODE"] = "disabled"
286+
env["DD_INJECT_FORCE"] = "true"
287+
env["DD_INJECTION_ENABLED"] = "tracer"
286288

287289
# By default telemetry collection is enabled after 10 seconds, so we either need to
288290
# to sleep for 10 seconds or manually call _app_started() to generate the app started event.
@@ -403,6 +405,7 @@ def test_app_started_event_configuration_override(test_agent_session, run_python
403405
{"name": "DD_IAST_STACK_TRACE_ENABLED", "origin": "default", "value": True},
404406
{"name": "DD_IAST_TELEMETRY_VERBOSITY", "origin": "default", "value": "INFORMATION"},
405407
{"name": "DD_IAST_VULNERABILITIES_PER_REQUEST", "origin": "default", "value": 2},
408+
{"name": "DD_INJECTION_ENABLED", "origin": "env_var", "value": "tracer"},
406409
{"name": "DD_INJECT_FORCE", "origin": "env_var", "value": True},
407410
{"name": "DD_INSTRUMENTATION_INSTALL_ID", "origin": "default", "value": None},
408411
{"name": "DD_INSTRUMENTATION_INSTALL_TYPE", "origin": "default", "value": None},
@@ -524,10 +527,8 @@ def test_app_started_event_configuration_override(test_agent_session, run_python
524527
{"name": "DD_VERSION", "origin": "default", "value": None},
525528
{"name": "_DD_APPSEC_DEDUPLICATION_ENABLED", "origin": "default", "value": True},
526529
{"name": "_DD_IAST_LAZY_TAINT", "origin": "default", "value": False},
527-
{"name": "_DD_INJECT_WAS_ATTEMPTED", "origin": "default", "value": False},
528530
{"name": "_DD_TRACE_WRITER_LOG_ERROR_PAYLOADS", "origin": "default", "value": False},
529-
{"name": "ddtrace_auto_used", "origin": "unknown", "value": True},
530-
{"name": "ddtrace_bootstrapped", "origin": "unknown", "value": True},
531+
{"name": "instrumentation_source", "origin": "code", "value": "manual"},
531532
{"name": "python_build_gnu_type", "origin": "unknown", "value": sysconfig.get_config_var("BUILD_GNU_TYPE")},
532533
{"name": "python_host_gnu_type", "origin": "unknown", "value": sysconfig.get_config_var("HOST_GNU_TYPE")},
533534
{"name": "python_soabi", "origin": "unknown", "value": sysconfig.get_config_var("SOABI")},
@@ -549,6 +550,30 @@ def test_update_dependencies_event(test_agent_session, ddtrace_run_python_code_i
549550
assert len(deps) == 1, deps
550551

551552

553+
def test_instrumentation_source_config(
554+
test_agent_session, ddtrace_run_python_code_in_subprocess, run_python_code_in_subprocess
555+
):
556+
env = os.environ.copy()
557+
env["_DD_INSTRUMENTATION_TELEMETRY_TESTS_FORCE_APP_STARTED"] = "true"
558+
559+
_, stderr, status, _ = call_program("ddtrace-run", sys.executable, "-c", "", env=env)
560+
assert status == 0, stderr
561+
configs = test_agent_session.get_configurations("instrumentation_source")
562+
assert configs and configs[-1]["value"] == "cmd_line"
563+
test_agent_session.clear()
564+
565+
_, stderr, status, _ = call_program(sys.executable, "-c", "import ddtrace.auto", env=env)
566+
assert status == 0, stderr
567+
configs = test_agent_session.get_configurations("instrumentation_source")
568+
assert configs and configs[-1]["value"] == "manual"
569+
test_agent_session.clear()
570+
571+
_, stderr, status, _ = call_program(sys.executable, "-c", "import ddtrace", env=env)
572+
assert status == 0, stderr
573+
configs = test_agent_session.get_configurations("instrumentation_source")
574+
assert not configs, "instrumentation_source should not be set when ddtrace instrumentation is not used"
575+
576+
552577
def test_update_dependencies_event_when_disabled(test_agent_session, ddtrace_run_python_code_in_subprocess):
553578
env = os.environ.copy()
554579
# app-started events are sent 10 seconds after ddtrace imported, this configuration overrides this

0 commit comments

Comments
 (0)