Skip to content

Commit 8582da5

Browse files
authored
opentelemetry-instrumentation: don't fail if an instrumentor raises ImportError (#2923)
1 parent 779ec9e commit 8582da5

File tree

3 files changed

+77
-0
lines changed

3 files changed

+77
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3737

3838
- Deprecation of pkg_resource in favor of importlib.metadata
3939
([#2871](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2871))
40+
- `opentelemetry-instrumentation` Don't fail distro loading if instrumentor raises ImportError, instead skip them
41+
([#2923](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/2923))
4042

4143
## Version 1.27.0/0.48b0 (2024-08-28)
4244

opentelemetry-instrumentation/src/opentelemetry/instrumentation/auto_instrumentation/_load.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,17 @@ def _load_instrumentors(distro):
113113
# tell instrumentation to not run dep checks again as we already did it above
114114
distro.load_instrumentor(entry_point, skip_dep_check=True)
115115
_logger.debug("Instrumented %s", entry_point.name)
116+
except ImportError:
117+
# in scenarios using the kubernetes operator to do autoinstrumentation some
118+
# instrumentors (usually requiring binary extensions) may fail to load
119+
# because the injected autoinstrumentation code does not match the application
120+
# environment regarding python version, libc, etc... In this case it's better
121+
# to skip the single instrumentation rather than failing to load everything
122+
# so treat differently ImportError than the rest of exceptions
123+
_logger.exception(
124+
"Importing of %s failed, skipping it", entry_point.name
125+
)
126+
continue
116127
except Exception as exc: # pylint: disable=broad-except
117128
_logger.exception("Instrumenting of %s failed", entry_point.name)
118129
raise exc

opentelemetry-instrumentation/tests/auto_instrumentation/test_load.py

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,70 @@ def test_load_instrumentors_dep_conflict(self, iter_mock, dep_mock): # pylint:
310310
)
311311
distro_mock.load_instrumentor.assert_called_once()
312312

313+
@patch(
314+
"opentelemetry.instrumentation.auto_instrumentation._load.get_dist_dependency_conflicts"
315+
)
316+
@patch(
317+
"opentelemetry.instrumentation.auto_instrumentation._load.entry_points"
318+
)
319+
def test_load_instrumentors_import_error_does_not_stop_everything(
320+
self, iter_mock, dep_mock
321+
):
322+
ep_mock1 = Mock(name="instr1")
323+
ep_mock2 = Mock(name="instr2")
324+
325+
distro_mock = Mock()
326+
distro_mock.load_instrumentor.side_effect = [ImportError, None]
327+
328+
# Mock entry points in order
329+
iter_mock.side_effect = [
330+
(),
331+
(ep_mock1, ep_mock2),
332+
(),
333+
]
334+
dep_mock.return_value = None
335+
336+
_load._load_instrumentors(distro_mock)
337+
338+
distro_mock.load_instrumentor.assert_has_calls(
339+
[
340+
call(ep_mock1, skip_dep_check=True),
341+
call(ep_mock2, skip_dep_check=True),
342+
]
343+
)
344+
self.assertEqual(distro_mock.load_instrumentor.call_count, 2)
345+
346+
@patch(
347+
"opentelemetry.instrumentation.auto_instrumentation._load.get_dist_dependency_conflicts"
348+
)
349+
@patch(
350+
"opentelemetry.instrumentation.auto_instrumentation._load.entry_points"
351+
)
352+
def test_load_instrumentors_raises_exception(self, iter_mock, dep_mock):
353+
ep_mock1 = Mock(name="instr1")
354+
ep_mock2 = Mock(name="instr2")
355+
356+
distro_mock = Mock()
357+
distro_mock.load_instrumentor.side_effect = [ValueError, None]
358+
359+
# Mock entry points in order
360+
iter_mock.side_effect = [
361+
(),
362+
(ep_mock1, ep_mock2),
363+
(),
364+
]
365+
dep_mock.return_value = None
366+
367+
with self.assertRaises(ValueError):
368+
_load._load_instrumentors(distro_mock)
369+
370+
distro_mock.load_instrumentor.assert_has_calls(
371+
[
372+
call(ep_mock1, skip_dep_check=True),
373+
]
374+
)
375+
self.assertEqual(distro_mock.load_instrumentor.call_count, 1)
376+
313377
def test_load_instrumentors_no_entry_point_mocks(self):
314378
distro_mock = Mock()
315379
_load._load_instrumentors(distro_mock)

0 commit comments

Comments
 (0)