diff --git a/CHANGELOG.md b/CHANGELOG.md index d307c623..07dfba9b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ - Upgrade to exporter 1.0.0b14 and OTel 1.18 ([#295](https://github.com/microsoft/ApplicationInsights-Python/pull/295)) +- Enable Azure Core Tracing OpenTelemetry plugin + ([#269](https://github.com/microsoft/ApplicationInsights-Python/pull/269)) ## [1.0.0b13](https://github.com/microsoft/ApplicationInsights-Python/releases/tag/v1.0.0b13) - 2023-06-14 diff --git a/azure-monitor-opentelemetry/README.md b/azure-monitor-opentelemetry/README.md index e5f3e4de..1c148eaf 100644 --- a/azure-monitor-opentelemetry/README.md +++ b/azure-monitor-opentelemetry/README.md @@ -21,6 +21,10 @@ OpenTelemetry instrumentations allow automatic collection of requests sent from | [OpenTelemetry UrlLib Instrumentation][ot_instrumentation_urllib] | [urllib][pypi_urllib] | All | [OpenTelemetry UrlLib3 Instrumentation][ot_instrumentation_urllib3] | [urllib3][pypi_urllib3] | [link][ot_instrumentation_urllib3_version] +## Azure Core Distributed Tracing + +Using the [Azure Core Tracing OpenTelemetry][azure_core_tracing_opentelemetry_plugin] library, you can automatically capture the distributed tracing from Azure Core libraries. See the associated [sample][azure_core_tracing_opentelemetry_plugin_sample] for more information. This feature is enabled automatically. + ## Getting started ### Key Concepts @@ -68,8 +72,7 @@ You can configure further with [OpenTelemetry environment variables][ot_env_vars | `OTEL_BSP_SCHEDULE_DELAY` | Specifies the distributed tracing export interval in milliseconds. Defaults to 5000. | | `OTEL_TRACES_SAMPLER_ARG` | Specifies the ratio of distributed tracing telemetry to be [sampled][application_insights_sampling]. Accepted values are in the range [0,1]. Defaults to 1.0, meaning no telemetry is sampled out. | | `OTEL_PYTHON_DISABLED_INSTRUMENTATIONS` | Specifies which of the supported instrumentations to disable. Disabled instrumentations will not be instrumented as part of `configure_azure_monitor`. However, they can still be manually instrumented by users after the fact. Accepts a comma-separated list of lowercase entry point names for instrumentations. For example, set to `"psycopg2,fastapi"` to disable the Psycopg2 and FastAPI instrumentations. Defaults to an empty list, enabling all supported instrumentations. | - - + #### Azure monitor OpenTelemetry Exporter configurations @@ -95,6 +98,8 @@ Samples are available [here][samples] to demonstrate how to utilize the above co * [OpenTelemetry Python Official Docs][ot_python_docs] +[azure_core_tracing_opentelemetry_plugin]:https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/core/azure-core-tracing-opentelemetry +[azure_core_tracing_opentelemetry_plugin_sample]: https://github.com/microsoft/ApplicationInsights-Python/tree/main/azure-monitor-opentelemetry/samples/tracing/azure_core.py [azure_monitor_opentelemetry]: https://learn.microsoft.com/azure/azure-monitor/app/opentelemetry-enable?tabs=python [azure_monitor_opentelemetry_exporters]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry-exporter#microsoft-opentelemetry-exporter-for-azure-monitor [azure_portal]: https://portal.azure.com diff --git a/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_configure.py b/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_configure.py index db9fe879..9538a386 100644 --- a/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_configure.py +++ b/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_configure.py @@ -6,7 +6,10 @@ from logging import getLogger from typing import Dict +from azure.core.settings import settings +from azure.core.tracing.ext.opentelemetry_span import OpenTelemetrySpan from azure.monitor.opentelemetry._constants import ( + DISABLE_AZURE_CORE_TRACING_ARG, DISABLE_LOGGING_ARG, DISABLE_METRICS_ARG, DISABLE_TRACING_ARG, @@ -104,6 +107,9 @@ def _setup_tracing(configurations: Dict[str, ConfigurationValue]): trace_exporter, ) get_tracer_provider().add_span_processor(span_processor) + disable_azure_core_tracing = configurations[DISABLE_AZURE_CORE_TRACING_ARG] + if not disable_azure_core_tracing: + settings.tracing_implementation = OpenTelemetrySpan def _setup_logging(configurations: Dict[str, ConfigurationValue]): diff --git a/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_constants.py b/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_constants.py index ddcf5896..d0d72c70 100644 --- a/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_constants.py +++ b/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_constants.py @@ -16,6 +16,7 @@ # --------------------Configuration------------------------------------------ CONNECTION_STRING_ARG = "connection_string" +DISABLE_AZURE_CORE_TRACING_ARG = "disable_azure_core_tracing" DISABLE_LOGGING_ARG = "disable_logging" DISABLE_METRICS_ARG = "disable_metrics" DISABLE_TRACING_ARG = "disable_tracing" diff --git a/azure-monitor-opentelemetry/azure/monitor/opentelemetry/autoinstrumentation/_distro.py b/azure-monitor-opentelemetry/azure/monitor/opentelemetry/autoinstrumentation/_distro.py index 32013c9b..7d1af773 100644 --- a/azure-monitor-opentelemetry/azure/monitor/opentelemetry/autoinstrumentation/_distro.py +++ b/azure-monitor-opentelemetry/azure/monitor/opentelemetry/autoinstrumentation/_distro.py @@ -6,6 +6,8 @@ import logging from os import environ +from azure.core.settings import settings +from azure.core.tracing.ext.opentelemetry_span import OpenTelemetrySpan from azure.monitor.opentelemetry._vendor.v0_39b0.opentelemetry.instrumentation.distro import ( BaseDistro, ) @@ -46,12 +48,6 @@ def _configure_auto_instrumentation() -> None: AzureStatusLogger.log_status(False, "Distro being configured.") AzureDiagnosticLogging.enable(_logger) AzureDiagnosticLogging.enable(_opentelemetry_logger) - # TODO: Enabled when duplicate logging issue is solved - # if _EXPORTER_DIAGNOSTICS_ENABLED: - # exporter_logger = logging.getLogger( - # "azure.monitor.opentelemetry.exporter" - # ) - # AzureDiagnosticLogging.enable(_exporter_logger) environ.setdefault( OTEL_METRICS_EXPORTER, "azure_monitor_opentelemetry_exporter" ) @@ -64,6 +60,7 @@ def _configure_auto_instrumentation() -> None: environ.setdefault( _OTEL_PYTHON_LOGGING_AUTO_INSTRUMENTATION_ENABLED, "true" ) + settings.tracing_implementation = OpenTelemetrySpan AzureStatusLogger.log_status(True) _logger.info( "Azure Monitor OpenTelemetry Distro configured successfully." diff --git a/azure-monitor-opentelemetry/azure/monitor/opentelemetry/util/configurations.py b/azure-monitor-opentelemetry/azure/monitor/opentelemetry/util/configurations.py index 9f55b4c1..e5a37523 100644 --- a/azure-monitor-opentelemetry/azure/monitor/opentelemetry/util/configurations.py +++ b/azure-monitor-opentelemetry/azure/monitor/opentelemetry/util/configurations.py @@ -9,6 +9,7 @@ from typing import Dict from azure.monitor.opentelemetry._constants import ( + DISABLE_AZURE_CORE_TRACING_ARG, DISABLE_LOGGING_ARG, DISABLE_METRICS_ARG, DISABLE_TRACING_ARG, @@ -52,6 +53,7 @@ def _get_configurations(**kwargs) -> Dict[str, ConfigurationValue]: _default_disabled_instrumentations(configurations) _default_logging_export_interval_ms(configurations) _default_sampling_ratio(configurations) + _default_disable_azure_core_tracing(configurations) # TODO: remove when validation added to BLRP if configurations[LOGGING_EXPORT_INTERVAL_MS_ARG] <= 0: @@ -125,3 +127,8 @@ def _default_sampling_ratio(configurations): _INVALID_FLOAT_MESSAGE, SAMPLING_RATIO_ENV_VAR, default, e ) configurations[SAMPLING_RATIO_ARG] = default + + +# TODO: Placeholder for future configuration +def _default_disable_azure_core_tracing(configurations): + configurations[DISABLE_AZURE_CORE_TRACING_ARG] = False diff --git a/azure-monitor-opentelemetry/samples/tracing/azure_core.py b/azure-monitor-opentelemetry/samples/tracing/azure_core.py new file mode 100644 index 00000000..2dbc1647 --- /dev/null +++ b/azure-monitor-opentelemetry/samples/tracing/azure_core.py @@ -0,0 +1,18 @@ +from os import environ + +from azure.monitor.opentelemetry import configure_azure_monitor +from opentelemetry import trace + +# Set up exporting to Azure Monitor +configure_azure_monitor() + +# Example with Storage SDKs + +from azure.storage.blob import BlobServiceClient + +tracer = trace.get_tracer(__name__) +with tracer.start_as_current_span(name="MyApplication"): + client = BlobServiceClient.from_connection_string( + environ["AZURE_STORAGE_ACCOUNT_CONNECTION_STRING"] + ) + client.create_container("mycontainer") # Call will be traced diff --git a/azure-monitor-opentelemetry/setup.py b/azure-monitor-opentelemetry/setup.py index b3cb0b2b..e50d6219 100644 --- a/azure-monitor-opentelemetry/setup.py +++ b/azure-monitor-opentelemetry/setup.py @@ -84,6 +84,7 @@ }, python_requires=">=3.7", install_requires=[ + "azure-core-tracing-opentelemetry==1.0.0b9", "azure-monitor-opentelemetry-exporter>=1.0.0b14", "opentelemetry-api==1.18.0", "opentelemetry-sdk==1.18.0", diff --git a/azure-monitor-opentelemetry/tests/autoinstrumentation/test_distro.py b/azure-monitor-opentelemetry/tests/autoinstrumentation/test_distro.py index 874ca922..f161506f 100644 --- a/azure-monitor-opentelemetry/tests/autoinstrumentation/test_distro.py +++ b/azure-monitor-opentelemetry/tests/autoinstrumentation/test_distro.py @@ -1,34 +1,21 @@ from unittest import TestCase from unittest.mock import patch +from azure.core.tracing.ext.opentelemetry_span import OpenTelemetrySpan from azure.monitor.opentelemetry.autoinstrumentation._distro import ( AzureMonitorDistro, ) class TestDistro(TestCase): + @patch("azure.monitor.opentelemetry.autoinstrumentation._distro.settings") @patch( "azure.monitor.opentelemetry.autoinstrumentation._distro.AzureDiagnosticLogging.enable" ) - # TODO: Enabled when duplicate logging issue is solved - # @patch( - # "azure.monitor.opentelemetry.autoinstrumentation._diagnostic_logging._EXPORTER_DIAGNOSTICS_ENABLED", - # False, - # ) - def test_configure(self, mock_diagnostics): + def test_configure(self, mock_diagnostics, azure_core_mock): distro = AzureMonitorDistro() distro.configure() self.assertEqual(mock_diagnostics.call_count, 2) - - # TODO: Enabled when duplicate logging issue is solved - # @patch( - # "azure.monitor.opentelemetry.autoinstrumentation._distro.AzureDiagnosticLogging.enable" - # ) - # @patch( - # "azure.monitor.opentelemetry.autoinstrumentation._diagnostic_logging._EXPORTER_DIAGNOSTICS_ENABLED", - # True, - # ) - # def test_configure_exporter_diagnostics(self, mock_diagnostics): - # distro = AzureMonitorDistro() - # distro.configure() - # self.assertEqual(mock_diagnostics.call_count, 3) + self.assertEqual( + azure_core_mock.tracing_implementation, OpenTelemetrySpan + ) diff --git a/azure-monitor-opentelemetry/tests/configuration/test_configure.py b/azure-monitor-opentelemetry/tests/configuration/test_configure.py index 70102ed6..40c5b618 100644 --- a/azure-monitor-opentelemetry/tests/configuration/test_configure.py +++ b/azure-monitor-opentelemetry/tests/configuration/test_configure.py @@ -15,6 +15,7 @@ import unittest from unittest.mock import Mock, patch +from azure.core.tracing.ext.opentelemetry_span import OpenTelemetrySpan from azure.monitor.opentelemetry._configure import ( _SUPPORTED_INSTRUMENTED_LIBRARIES_DEPENDENCIES_MAP, _setup_instrumentations, @@ -162,6 +163,9 @@ def test_configure_azure_monitor_disable_metrics( metrics_mock.assert_not_called() instrumentation_mock.assert_called_once_with(configurations) + @patch( + "azure.monitor.opentelemetry._configure.settings", + ) @patch( "azure.monitor.opentelemetry._configure.BatchSpanProcessor", ) @@ -189,6 +193,7 @@ def test_setup_tracing( get_tracer_provider_mock, trace_exporter_mock, bsp_mock, + azure_core_mock, ): sampler_init_mock = Mock() sampler_mock.return_value = sampler_init_mock @@ -202,6 +207,7 @@ def test_setup_tracing( configurations = { "connection_string": "test_cs", + "disable_azure_core_tracing": False, "sampling_ratio": 0.5, } _setup_tracing(configurations) @@ -214,6 +220,9 @@ def test_setup_tracing( trace_exporter_mock.assert_called_once_with(**configurations) bsp_mock.assert_called_once_with(trace_exp_init_mock) tp_init_mock.add_span_processor.assert_called_once_with(bsp_init_mock) + self.assertEqual( + azure_core_mock.tracing_implementation, OpenTelemetrySpan + ) @patch( "azure.monitor.opentelemetry._configure.getLogger", diff --git a/azure-monitor-opentelemetry/tests/configuration/test_util.py b/azure-monitor-opentelemetry/tests/configuration/test_util.py index 73d79749..48487c1d 100644 --- a/azure-monitor-opentelemetry/tests/configuration/test_util.py +++ b/azure-monitor-opentelemetry/tests/configuration/test_util.py @@ -38,6 +38,7 @@ def test_get_configurations(self): ) self.assertEqual(configurations["connection_string"], "test_cs") + self.assertEqual(configurations["disable_azure_core_tracing"], False) self.assertEqual(configurations["disable_logging"], False) self.assertEqual(configurations["disable_metrics"], False) self.assertEqual(configurations["disable_tracing"], False) @@ -52,6 +53,7 @@ def test_get_configurations_defaults(self): configurations = _get_configurations() self.assertTrue("connection_string" not in configurations) + self.assertEqual(configurations["disable_azure_core_tracing"], False) self.assertEqual(configurations["disable_logging"], False) self.assertEqual(configurations["disable_metrics"], False) self.assertEqual(configurations["disable_tracing"], False) @@ -87,6 +89,7 @@ def test_get_configurations_env_vars(self): configurations = _get_configurations() self.assertTrue("connection_string" not in configurations) + self.assertEqual(configurations["disable_azure_core_tracing"], False) self.assertEqual(configurations["disable_logging"], True) self.assertEqual(configurations["disable_metrics"], True) self.assertEqual(configurations["disable_tracing"], True) @@ -112,6 +115,7 @@ def test_get_configurations_env_vars_validation(self): configurations = _get_configurations() self.assertTrue("connection_string" not in configurations) + self.assertEqual(configurations["disable_azure_core_tracing"], False) self.assertEqual(configurations["disable_logging"], False) self.assertEqual(configurations["disable_metrics"], False) self.assertEqual(configurations["disable_tracing"], False)