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
10 changes: 4 additions & 6 deletions sdk/monitor/azure-monitor-opentelemetry/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,18 @@

## 1.6.8 (Unreleased)

### Features Added

### Breaking Changes

### Bugs Fixed

- Pin OTel before breaking change.
([#40529](https://github.com/Azure/azure-sdk-for-python/pull/40529))

### Other Changes

## 1.6.7 (2025-04-10)

### Bugs Fixed

- Adapt to OpenTelemetry dependency resolver change.
([#40463](https://github.com/Azure/azure-sdk-for-python/pull/40463))
### Other Changes

## 1.6.6 (2025-04-07)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@
# Licensed under the MIT License. See License in the project root for
# license information.
# --------------------------------------------------------------------------
from functools import cached_property
from logging import getLogger, Formatter
from typing import Dict, List, cast

from opentelemetry._events import _set_event_logger_provider
from opentelemetry._logs import set_logger_provider
from opentelemetry.instrumentation.dependencies import (
DependencyConflictError,
get_dist_dependency_conflicts,
)
from opentelemetry.instrumentation.instrumentor import ( # type: ignore
BaseInstrumentor,
Expand All @@ -25,8 +26,11 @@
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.trace import set_tracer_provider
from opentelemetry.util._importlib_metadata import entry_points

from opentelemetry.util._importlib_metadata import (
EntryPoint,
distributions,
entry_points,
)

from azure.core.settings import settings
from azure.core.tracing.ext.opentelemetry_span import OpenTelemetrySpan
Expand Down Expand Up @@ -207,8 +211,25 @@ def _setup_live_metrics(configurations):
enable_live_metrics(**configurations)


class _EntryPointDistFinder:
@cached_property
def _mapping(self):
return {self._key_for(ep): dist for dist in distributions() for ep in dist.entry_points}

def dist_for(self, entry_point: EntryPoint):
dist = getattr(entry_point, "dist", None)
if dist:
return dist

return self._mapping.get(self._key_for(entry_point))

@staticmethod
def _key_for(entry_point: EntryPoint):
return f"{entry_point.group}:{entry_point.name}:{entry_point.value}"


def _setup_instrumentations(configurations: Dict[str, ConfigurationValue]):
# entry_point_finder = _EntryPointDistFinder()
entry_point_finder = _EntryPointDistFinder()
# use pkg_resources for now until https://github.com/open-telemetry/opentelemetry-python/pull/3168 is merged
for entry_point in entry_points(group="opentelemetry_instrumentor"):
lib_name = entry_point.name
Expand All @@ -218,16 +239,20 @@ def _setup_instrumentations(configurations: Dict[str, ConfigurationValue]):
_logger.debug("Instrumentation skipped for library %s", entry_point.name)
continue
try:
# Check if dependent libraries/version are installed
entry_point_dist = entry_point_finder.dist_for(entry_point) # type: ignore
conflict = get_dist_dependency_conflicts(entry_point_dist) # type: ignore
if conflict:
_logger.debug(
"Skipping instrumentation %s: %s",
entry_point.name,
conflict,
)
continue
# Load the instrumentor via entrypoint
instrumentor: BaseInstrumentor = entry_point.load()
instrumentor().instrument(raise_exception_on_conflict=True)
except DependencyConflictError as exc:
_logger.debug(
"Skipping instrumentation %s: %s",
entry_point.name,
exc.conflict,
)
continue
# tell instrumentation to not run dep checks again as we already did it above
instrumentor().instrument(skip_dep_check=True)
except Exception as ex: # pylint: disable=broad-except
_logger.warning(
"Exception occurred when instrumenting: %s.",
Expand Down
17 changes: 9 additions & 8 deletions sdk/monitor/azure-monitor-opentelemetry/setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -84,15 +84,16 @@
"azure-core<2.0.0,>=1.28.0",
"azure-core-tracing-opentelemetry~=1.0.0b11",
"azure-monitor-opentelemetry-exporter~=1.0.0b31",
"opentelemetry-instrumentation-django~=0.53b0",
"opentelemetry-instrumentation-fastapi~=0.53b0",
"opentelemetry-instrumentation-flask~=0.53b0",
"opentelemetry-instrumentation-psycopg2~=0.53b0",
"opentelemetry-instrumentation-requests~=0.53b0",
"opentelemetry-instrumentation-urllib~=0.53b0",
"opentelemetry-instrumentation-urllib3~=0.53b0",
# TODO: Unpin once breaking change in 1.32.0/0.53b0 is resolved.
"opentelemetry-instrumentation-django<0.53b0,>=0.49b0",
"opentelemetry-instrumentation-fastapi<0.53b0,>=0.49b0",
"opentelemetry-instrumentation-flask<0.53b0,>=0.49b0",
"opentelemetry-instrumentation-psycopg2<0.53b0,>=0.49b0",
"opentelemetry-instrumentation-requests<0.53b0,>=0.49b0",
"opentelemetry-instrumentation-urllib<0.53b0,>=0.49b0",
"opentelemetry-instrumentation-urllib3<0.53b0,>=0.49b0",
"opentelemetry-resource-detector-azure~=0.1.4",
"opentelemetry-sdk~=1.32",
"opentelemetry-sdk<1.32,>=1.28.0",
],
entry_points={
"opentelemetry_distro": [
Expand Down
29 changes: 18 additions & 11 deletions sdk/monitor/azure-monitor-opentelemetry/tests/test_configure.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,6 @@
import unittest
from unittest.mock import Mock, call, patch

from opentelemetry.instrumentation.dependencies import (
DependencyConflict,
DependencyConflictError,
)
from opentelemetry.sdk._logs import LoggingHandler
from opentelemetry.sdk.resources import Resource

Expand Down Expand Up @@ -537,10 +533,12 @@ def test_setup_live_metrics(

@patch("azure.monitor.opentelemetry._configure._ALL_SUPPORTED_INSTRUMENTED_LIBRARIES", ("test_instr2"))
@patch("azure.monitor.opentelemetry._configure._is_instrumentation_enabled")
@patch("azure.monitor.opentelemetry._configure.get_dist_dependency_conflicts")
@patch("azure.monitor.opentelemetry._configure.entry_points")
def test_setup_instrumentations_lib_not_supported(
self,
iter_mock,
dep_mock,
enabled_mock,
):
ep_mock = Mock()
Expand All @@ -552,8 +550,10 @@ def test_setup_instrumentations_lib_not_supported(
ep_mock.name = "test_instr1"
ep2_mock.name = "test_instr2"
ep2_mock.load.return_value = instr_class_mock
dep_mock.return_value = None
enabled_mock.return_value = True
_setup_instrumentations({})
dep_mock.assert_called_with(ep2_mock.dist)
ep_mock.load.assert_not_called()
ep2_mock.load.assert_called_once()
instrumentor_mock.instrument.assert_called_once()
Expand All @@ -579,10 +579,12 @@ def test_setup_instrumentations_additional_azure(
@patch("azure.monitor.opentelemetry._configure._ALL_SUPPORTED_INSTRUMENTED_LIBRARIES", ("test_instr"))
@patch("azure.monitor.opentelemetry._configure._is_instrumentation_enabled")
@patch("azure.monitor.opentelemetry._configure._logger")
@patch("azure.monitor.opentelemetry._configure.get_dist_dependency_conflicts")
@patch("azure.monitor.opentelemetry._configure.entry_points")
def test_setup_instrumentations_conflict(
self,
iter_mock,
dep_mock,
logger_mock,
enabled_mock,
):
Expand All @@ -593,24 +595,23 @@ def test_setup_instrumentations_conflict(
instr_class_mock.return_value = instrumentor_mock
ep_mock.name = "test_instr"
ep_mock.load.return_value = instr_class_mock
instrumentor_mock.instrument.side_effect = DependencyConflictError(
DependencyConflict(
required="test_instr"
)
)
dep_mock.return_value = True
enabled_mock.return_value = True
_setup_instrumentations({})
ep_mock.load.assert_called_once()
instrumentor_mock.instrument.assert_called_once()
dep_mock.assert_called_with(ep_mock.dist)
ep_mock.load.assert_not_called()
instrumentor_mock.instrument.assert_not_called()
logger_mock.debug.assert_called_once()

@patch("azure.monitor.opentelemetry._configure._ALL_SUPPORTED_INSTRUMENTED_LIBRARIES", ("test_instr"))
@patch("azure.monitor.opentelemetry._configure._is_instrumentation_enabled")
@patch("azure.monitor.opentelemetry._configure._logger")
@patch("azure.monitor.opentelemetry._configure.get_dist_dependency_conflicts")
@patch("azure.monitor.opentelemetry._configure.entry_points")
def test_setup_instrumentations_exception(
self,
iter_mock,
dep_mock,
logger_mock,
enabled_mock,
):
Expand All @@ -621,8 +622,10 @@ def test_setup_instrumentations_exception(
instr_class_mock.return_value = instrumentor_mock
ep_mock.name = "test_instr"
ep_mock.load.side_effect = Exception()
dep_mock.return_value = None
enabled_mock.return_value = True
_setup_instrumentations({})
dep_mock.assert_called_with(ep_mock.dist)
ep_mock.load.assert_called_once()
instrumentor_mock.instrument.assert_not_called()
logger_mock.warning.assert_called_once()
Expand All @@ -632,10 +635,12 @@ def test_setup_instrumentations_exception(
)
@patch("azure.monitor.opentelemetry._configure._is_instrumentation_enabled")
@patch("azure.monitor.opentelemetry._configure._logger")
@patch("azure.monitor.opentelemetry._configure.get_dist_dependency_conflicts")
@patch("azure.monitor.opentelemetry._configure.entry_points")
def test_setup_instrumentations_disabled(
self,
iter_mock,
dep_mock,
logger_mock,
enabled_mock,
):
Expand All @@ -648,8 +653,10 @@ def test_setup_instrumentations_disabled(
ep_mock.name = "test_instr1"
ep2_mock.name = "test_instr2"
ep2_mock.load.return_value = instr_class_mock
dep_mock.return_value = None
enabled_mock.side_effect = [False, True]
_setup_instrumentations({})
dep_mock.assert_called_with(ep2_mock.dist)
ep_mock.load.assert_not_called()
ep2_mock.load.assert_called_once()
instrumentor_mock.instrument.assert_called_once()
Expand Down