From 6f09b582a4100e4306b365728ab393824f041c0b Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 5 May 2026 21:04:23 +0000 Subject: [PATCH 1/4] Pass enable_sensitive_data=True to enable_instrumentation() Agent-Logs-Url: https://github.com/microsoft/opentelemetry-distro-python/sessions/baa487e4-f808-401a-bf89-e8680bf283a2 Co-authored-by: singankit <30610298+singankit@users.noreply.github.com> --- .../opentelemetry/_agent_framework/_trace_instrumentor.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/microsoft/opentelemetry/_agent_framework/_trace_instrumentor.py b/src/microsoft/opentelemetry/_agent_framework/_trace_instrumentor.py index f8651a8d..6405e663 100644 --- a/src/microsoft/opentelemetry/_agent_framework/_trace_instrumentor.py +++ b/src/microsoft/opentelemetry/_agent_framework/_trace_instrumentor.py @@ -19,7 +19,7 @@ class AgentFrameworkInstrumentor(BaseInstrumentor): """Instruments Agent Framework with OpenTelemetry observability. - Automatically calls ``agent_framework.observability.enable_instrumentation()`` + Automatically calls ``agent_framework.observability.enable_instrumentation(enable_sensitive_data=True)`` so the Agent Framework SDK emits OpenTelemetry spans. When the A365 span enricher pipeline is available, also registers a span enricher for attribute normalization. @@ -38,7 +38,7 @@ def _instrument(self, **kwargs: Any) -> None: try: from agent_framework.observability import enable_instrumentation - enable_instrumentation() + enable_instrumentation(enable_sensitive_data=True) self._af_instrumentation_enabled = True except ImportError as exc: _logger.debug( From b468fd5d0b744fd652be5918bb7723d0c919d43a Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Tue, 5 May 2026 21:27:33 +0000 Subject: [PATCH 2/4] Read AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED env var for enable_sensitive_data; add tests Agent-Logs-Url: https://github.com/microsoft/opentelemetry-distro-python/sessions/1bb4ef7c-4849-4345-8180-0855b15a4d2b Co-authored-by: singankit <30610298+singankit@users.noreply.github.com> --- .../_agent_framework/_trace_instrumentor.py | 18 +++++-- .../test_trace_instrumentor.py | 48 ++++++++++++++++++- 2 files changed, 60 insertions(+), 6 deletions(-) diff --git a/src/microsoft/opentelemetry/_agent_framework/_trace_instrumentor.py b/src/microsoft/opentelemetry/_agent_framework/_trace_instrumentor.py index 6405e663..75688980 100644 --- a/src/microsoft/opentelemetry/_agent_framework/_trace_instrumentor.py +++ b/src/microsoft/opentelemetry/_agent_framework/_trace_instrumentor.py @@ -4,6 +4,7 @@ from __future__ import annotations import logging +import os from collections.abc import Collection from typing import Any @@ -14,14 +15,18 @@ _logger = logging.getLogger(__name__) _instruments = ("agent-framework >= 1.0.0",) +_TRACE_CONTENTS_ENV = "AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED" class AgentFrameworkInstrumentor(BaseInstrumentor): """Instruments Agent Framework with OpenTelemetry observability. - Automatically calls ``agent_framework.observability.enable_instrumentation(enable_sensitive_data=True)`` - so the Agent Framework SDK emits OpenTelemetry spans. When the A365 span - enricher pipeline is available, also registers a span enricher for + Automatically calls ``agent_framework.observability.enable_instrumentation()`` + so the Agent Framework SDK emits OpenTelemetry spans. Sensitive data + (prompts, tool arguments, results) is included when the + ``AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED`` environment variable is + set to a truthy value (``true``, ``1``, ``yes``, or ``on``). When the A365 + span enricher pipeline is available, also registers a span enricher for attribute normalization. """ @@ -35,10 +40,15 @@ def instrumentation_dependencies(self) -> Collection[str]: def _instrument(self, **kwargs: Any) -> None: # Enable the Agent Framework SDK's built-in span generation so users # don't need to call enable_instrumentation() manually. + # Sensitive data recording mirrors the AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED + # environment variable, defaulting to False when unset. try: from agent_framework.observability import enable_instrumentation - enable_instrumentation(enable_sensitive_data=True) + enable_sensitive_data = os.environ.get(_TRACE_CONTENTS_ENV, "").strip().lower() in ( + "true", "1", "yes", "on" + ) + enable_instrumentation(enable_sensitive_data=enable_sensitive_data) self._af_instrumentation_enabled = True except ImportError as exc: _logger.debug( diff --git a/tests/agent_framework/test_trace_instrumentor.py b/tests/agent_framework/test_trace_instrumentor.py index 95d5ee88..b78ed4de 100644 --- a/tests/agent_framework/test_trace_instrumentor.py +++ b/tests/agent_framework/test_trace_instrumentor.py @@ -88,7 +88,51 @@ def test_instrument_calls_enable_instrumentation_when_available(self, mock_get_p instrumentor = AgentFrameworkInstrumentor() instrumentor._instrument() - mock_enable.assert_called_once() + mock_enable.assert_called_once_with(enable_sensitive_data=False) + self.assertTrue(instrumentor._af_instrumentation_enabled) + + @patch("microsoft.opentelemetry._agent_framework._trace_instrumentor.get_tracer_provider") + def test_instrument_enables_sensitive_data_when_env_var_set(self, mock_get_provider): + """When AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED is truthy, + enable_instrumentation must be called with enable_sensitive_data=True.""" + mock_get_provider.return_value = MagicMock() + mock_enable = MagicMock() + + import os + + with patch.dict("os.environ", {"AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED": "true"}): + with patch.dict( + "sys.modules", + { + "agent_framework": MagicMock(), + "agent_framework.observability": MagicMock(enable_instrumentation=mock_enable), + }, + ): + instrumentor = AgentFrameworkInstrumentor() + instrumentor._instrument() + + mock_enable.assert_called_once_with(enable_sensitive_data=True) + self.assertTrue(instrumentor._af_instrumentation_enabled) + + @patch("microsoft.opentelemetry._agent_framework._trace_instrumentor.get_tracer_provider") + def test_instrument_disables_sensitive_data_when_env_var_false(self, mock_get_provider): + """When AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED is falsy, + enable_instrumentation must be called with enable_sensitive_data=False.""" + mock_get_provider.return_value = MagicMock() + mock_enable = MagicMock() + + with patch.dict("os.environ", {"AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED": "false"}): + with patch.dict( + "sys.modules", + { + "agent_framework": MagicMock(), + "agent_framework.observability": MagicMock(enable_instrumentation=mock_enable), + }, + ): + instrumentor = AgentFrameworkInstrumentor() + instrumentor._instrument() + + mock_enable.assert_called_once_with(enable_sensitive_data=False) self.assertTrue(instrumentor._af_instrumentation_enabled) @patch("microsoft.opentelemetry._agent_framework._trace_instrumentor.get_tracer_provider") @@ -188,7 +232,7 @@ def test_enable_instrumentation_called_in_azure_monitor_only_scenario(self, mock instrumentor._instrument() # AF SDK enabled, span processor added, enricher NOT registered. - mock_enable.assert_called_once() + mock_enable.assert_called_once_with(enable_sensitive_data=False) self.assertTrue(instrumentor._af_instrumentation_enabled) mock_provider.add_span_processor.assert_called_once() self.assertFalse(instrumentor._owns_enricher) From 19a450e65b782cd5b0682204150860aea041cf6e Mon Sep 17 00:00:00 2001 From: Ankit Singhal Date: Wed, 6 May 2026 13:25:33 -0700 Subject: [PATCH 3/4] Add enable_sensitive_data param to use_microsoft_opentelemetry Pass enable_sensitive_data kwarg through to AgentFrameworkInstrumentor so the Agent Framework SDK can include sensitive span attributes (prompts, tool arguments, results) when explicitly enabled by the caller. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../_agent_framework/_trace_instrumentor.py | 15 ++---- src/microsoft/opentelemetry/_constants.py | 2 + src/microsoft/opentelemetry/_distro.py | 12 +++-- .../test_trace_instrumentor.py | 46 +++++-------------- 4 files changed, 25 insertions(+), 50 deletions(-) diff --git a/src/microsoft/opentelemetry/_agent_framework/_trace_instrumentor.py b/src/microsoft/opentelemetry/_agent_framework/_trace_instrumentor.py index 75688980..9e87068d 100644 --- a/src/microsoft/opentelemetry/_agent_framework/_trace_instrumentor.py +++ b/src/microsoft/opentelemetry/_agent_framework/_trace_instrumentor.py @@ -4,7 +4,6 @@ from __future__ import annotations import logging -import os from collections.abc import Collection from typing import Any @@ -15,18 +14,14 @@ _logger = logging.getLogger(__name__) _instruments = ("agent-framework >= 1.0.0",) -_TRACE_CONTENTS_ENV = "AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED" class AgentFrameworkInstrumentor(BaseInstrumentor): """Instruments Agent Framework with OpenTelemetry observability. Automatically calls ``agent_framework.observability.enable_instrumentation()`` - so the Agent Framework SDK emits OpenTelemetry spans. Sensitive data - (prompts, tool arguments, results) is included when the - ``AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED`` environment variable is - set to a truthy value (``true``, ``1``, ``yes``, or ``on``). When the A365 - span enricher pipeline is available, also registers a span enricher for + so the Agent Framework SDK emits OpenTelemetry spans. When the A365 span + enricher pipeline is available, also registers a span enricher for attribute normalization. """ @@ -40,14 +35,10 @@ def instrumentation_dependencies(self) -> Collection[str]: def _instrument(self, **kwargs: Any) -> None: # Enable the Agent Framework SDK's built-in span generation so users # don't need to call enable_instrumentation() manually. - # Sensitive data recording mirrors the AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED - # environment variable, defaulting to False when unset. + enable_sensitive_data = kwargs.get("enable_sensitive_data", False) try: from agent_framework.observability import enable_instrumentation - enable_sensitive_data = os.environ.get(_TRACE_CONTENTS_ENV, "").strip().lower() in ( - "true", "1", "yes", "on" - ) enable_instrumentation(enable_sensitive_data=enable_sensitive_data) self._af_instrumentation_enabled = True except ImportError as exc: diff --git a/src/microsoft/opentelemetry/_constants.py b/src/microsoft/opentelemetry/_constants.py index b1047c77..ce61c90d 100644 --- a/src/microsoft/opentelemetry/_constants.py +++ b/src/microsoft/opentelemetry/_constants.py @@ -86,6 +86,8 @@ # --- Spectra Sidecar Constants --- +ENABLE_SENSITIVE_DATA_ARG = "enable_sensitive_data" + ENABLE_SPECTRA_ARG = "enable_spectra" SPECTRA_ENDPOINT_ARG = "spectra_endpoint" SPECTRA_PROTOCOL_ARG = "spectra_protocol" diff --git a/src/microsoft/opentelemetry/_distro.py b/src/microsoft/opentelemetry/_distro.py index e181034d..ae96cf54 100644 --- a/src/microsoft/opentelemetry/_distro.py +++ b/src/microsoft/opentelemetry/_distro.py @@ -29,6 +29,7 @@ DISABLE_METRICS_ARG, DISABLE_TRACING_ARG, ENABLE_A365_ARG, + ENABLE_SENSITIVE_DATA_ARG, ENABLE_SPECTRA_ARG, SPECTRA_ENDPOINT_ARG, SPECTRA_PROTOCOL_ARG, @@ -186,6 +187,9 @@ def use_microsoft_opentelemetry(**kwargs: object) -> None: # pylint: disable=to ``SPECTRA_PROTOCOL`` env var. Defaults to ``"grpc"``. :keyword bool spectra_insecure: Use insecure (no TLS) connection. Defaults to True (localhost sidecar). + :keyword bool enable_sensitive_data: + Enable sensitive data recording (prompts, tool arguments, results) for + the Agent Framework SDK instrumentation. Defaults to False. :rtype: None """ @@ -209,6 +213,8 @@ def use_microsoft_opentelemetry(**kwargs: object) -> None: # pylint: disable=to spectra_protocol = kwargs.pop(SPECTRA_PROTOCOL_ARG, None) spectra_insecure = kwargs.pop(SPECTRA_INSECURE_ARG, None) + enable_sensitive_data: bool = bool(kwargs.pop(ENABLE_SENSITIVE_DATA_ARG, False)) + # Separate Azure Monitor kwargs from generic OTel kwargs otel_kwargs: Dict[str, Any] = {k: v for k, v in kwargs.items() if k not in _AZURE_MONITOR_KWARG_MAP} azure_monitor_kwargs: Dict[str, Any] = { @@ -311,7 +317,7 @@ def use_microsoft_opentelemetry(**kwargs: object) -> None: # pylint: disable=to set_logger_provider(logger_provider) # ---- Instrumentations (always, after providers are set) ---- - _setup_instrumentations(otel_kwargs) + _setup_instrumentations(otel_kwargs, **{ENABLE_SENSITIVE_DATA_ARG: enable_sensitive_data}) # ---- SDKStats manager (after providers, before returning) ---- _initialize_sdkstats(enable_azure_monitor) @@ -670,7 +676,7 @@ def _is_instrumentation_enabled(otel_kwargs: Dict[str, Any], lib_name: str) -> b return lib_options["enabled"] is True -def _setup_instrumentations(otel_kwargs: Dict[str, Any]) -> None: +def _setup_instrumentations(otel_kwargs: Dict[str, Any], **kwargs: Any) -> None: """Discover and activate OTel instrumentations for supported libraries.""" entry_point_finder = _EntryPointDistFinder() for entry_point in entry_points(group="opentelemetry_instrumentor"): @@ -691,7 +697,7 @@ def _setup_instrumentations(otel_kwargs: Dict[str, Any]) -> None: ) continue instrumentor: Any = entry_point.load() - instrumentor().instrument(skip_dep_check=True) + instrumentor().instrument(skip_dep_check=True, **kwargs) set_sdkstats_instrumentation_by_name(lib_name) except Exception as ex: # pylint: disable=broad-except _logger.warning( diff --git a/tests/agent_framework/test_trace_instrumentor.py b/tests/agent_framework/test_trace_instrumentor.py index b78ed4de..af95175f 100644 --- a/tests/agent_framework/test_trace_instrumentor.py +++ b/tests/agent_framework/test_trace_instrumentor.py @@ -92,49 +92,25 @@ def test_instrument_calls_enable_instrumentation_when_available(self, mock_get_p self.assertTrue(instrumentor._af_instrumentation_enabled) @patch("microsoft.opentelemetry._agent_framework._trace_instrumentor.get_tracer_provider") - def test_instrument_enables_sensitive_data_when_env_var_set(self, mock_get_provider): - """When AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED is truthy, + def test_instrument_enables_sensitive_data_when_kwarg_set(self, mock_get_provider): + """When enable_sensitive_data=True is passed as kwarg, enable_instrumentation must be called with enable_sensitive_data=True.""" mock_get_provider.return_value = MagicMock() mock_enable = MagicMock() - import os - - with patch.dict("os.environ", {"AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED": "true"}): - with patch.dict( - "sys.modules", - { - "agent_framework": MagicMock(), - "agent_framework.observability": MagicMock(enable_instrumentation=mock_enable), - }, - ): - instrumentor = AgentFrameworkInstrumentor() - instrumentor._instrument() + with patch.dict( + "sys.modules", + { + "agent_framework": MagicMock(), + "agent_framework.observability": MagicMock(enable_instrumentation=mock_enable), + }, + ): + instrumentor = AgentFrameworkInstrumentor() + instrumentor._instrument(enable_sensitive_data=True) mock_enable.assert_called_once_with(enable_sensitive_data=True) self.assertTrue(instrumentor._af_instrumentation_enabled) - @patch("microsoft.opentelemetry._agent_framework._trace_instrumentor.get_tracer_provider") - def test_instrument_disables_sensitive_data_when_env_var_false(self, mock_get_provider): - """When AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED is falsy, - enable_instrumentation must be called with enable_sensitive_data=False.""" - mock_get_provider.return_value = MagicMock() - mock_enable = MagicMock() - - with patch.dict("os.environ", {"AZURE_TRACING_GEN_AI_CONTENT_RECORDING_ENABLED": "false"}): - with patch.dict( - "sys.modules", - { - "agent_framework": MagicMock(), - "agent_framework.observability": MagicMock(enable_instrumentation=mock_enable), - }, - ): - instrumentor = AgentFrameworkInstrumentor() - instrumentor._instrument() - - mock_enable.assert_called_once_with(enable_sensitive_data=False) - self.assertTrue(instrumentor._af_instrumentation_enabled) - @patch("microsoft.opentelemetry._agent_framework._trace_instrumentor.get_tracer_provider") def test_instrument_skips_enable_when_af_not_installed(self, mock_get_provider): mock_get_provider.return_value = MagicMock() From 3e518840d6493971f4573750ea6740fcb1454414 Mon Sep 17 00:00:00 2001 From: Ankit Singhal Date: Wed, 6 May 2026 14:53:22 -0700 Subject: [PATCH 4/4] Document enable_sensitive_data in README All Options table Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 5fbbb8d8..945f3ef1 100644 --- a/README.md +++ b/README.md @@ -93,6 +93,7 @@ See the [A365 guide](A365_DOCUMENTATION.md) for A365-specific configuration. | `instrumentation_options` | `dict` | `None` | Per-library instrumentation enable/disable options. | | `enable_trace_based_sampling_for_logs` | `bool` | `False` | Enable trace-based sampling for logs. | | `enable_console` | `bool` | `False` | Console exporter (dev only). Auto-enables when no other exporter is active. | +| `enable_sensitive_data` | `bool` | `False` | Enable sensitive data recording (prompts, tool arguments, results) for Agent Framework SDK instrumentation. | | **Azure Monitor** | | | | | `enable_azure_monitor` | `bool` | `False` | Enable Azure Monitor export. | | `azure_monitor_connection_string` | `str` | `None` | Connection string. Also read from `APPLICATIONINSIGHTS_CONNECTION_STRING`. |