Skip to content

Commit 6f64589

Browse files
committed
opentelemetry-instrumentation: don't fail if an instrumentor raises ImportError
Treat instrumentor loading raising ImportError different than the other exceptions. In scenarios using the kubernetes operator to do autoinstrumentation some instrumentors may fail to load (usually requiring binary extensions) because the injected autoinstrumentation code does not match the application environment regarding python version, libc, etc... In this case it's better to skip the single instrumentation rather than failing to load everything so treat differently ImportError than the rest of exceptions.
1 parent beed0aa commit 6f64589

File tree

2 files changed

+75
-0
lines changed
  • opentelemetry-instrumentation
    • src/opentelemetry/instrumentation/auto_instrumentation
    • tests/auto_instrumentation

2 files changed

+75
-0
lines changed

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 may fail to load (usually requiring binary extensions)
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
@@ -316,6 +316,70 @@ def test_load_instrumentors_dep_conflict(
316316
)
317317
distro_mock.load_instrumentor.assert_called_once()
318318

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

0 commit comments

Comments
 (0)