open-telemetry / open-telemetry/opentelemetry-python-contrib

`google` namespace package not properly loaded with auto instrumentation

Open
#1,884 0 comments 3 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
Python
Stars
1.1k
Forks
1.1k
Avg merge
4d 15h
Merged PRs (30d)
16

Description

Describe your environment
Python 3.8.13
Linux (Ubuntu 22.04)

Steps to reproduce
Create the following Dockerfile:

FROM python:3.8.13-slim

RUN mkdir /otel-auto-instrumentation
RUN pip install -t /otel-auto-instrumentation/ opentelemetry-distro==0.39b0 opentelemetry-exporter-otlp-proto-http==1.18.0
ENV PYTHONPATH=/otel-auto-instrumentation/opentelemetry/instrumentation/auto_instrumentation:/otel-auto-instrumentation
ENV OTEL_EXPORTER_OTLP_METRICS_PROTOCOL=http/protobuf
ENV OTEL_EXPORTER_OTLP_TRACES_PROTOCOL=http/protobuf
RUN pip install google-auth
CMD python -c "import google.protobuf; print('import worked!')"

Build it with:

docker build -t otel-failing-import

Run it with:

docker run --rm otel-failing-import

What is the expected behavior?
The following should be displayed:
import worked!

What is the actual behavior?

Configuration of configurator failed
Traceback (most recent call last):
  File "/otel-auto-instrumentation/opentelemetry/instrumentation/auto_instrumentation/sitecustomize.py", line 105, in _load_configurators
    entry_point.load()().configure(auto_instrumentation_version=__version__)  # type: ignore
  File "/otel-auto-instrumentation/opentelemetry/sdk/_configuration/__init__.py", line 395, in configure
    self._configure(**kwargs)
  File "/otel-auto-instrumentation/opentelemetry/sdk/_configuration/__init__.py", line 411, in _configure
    _initialize_components(kwargs.get("auto_instrumentation_version"))
  File "/otel-auto-instrumentation/opentelemetry/sdk/_configuration/__init__.py", line 338, in _initialize_components
    trace_exporters, metric_exporters, log_exporters = _import_exporters(
  File "/otel-auto-instrumentation/opentelemetry/sdk/_configuration/__init__.py", line 259, in _import_exporters
    for (exporter_name, exporter_impl,) in _import_config_components(
  File "/otel-auto-instrumentation/opentelemetry/sdk/_configuration/__init__.py", line 101, in _import_config_components
    next(
  File "/otel-auto-instrumentation/importlib_metadata/__init__.py", line 208, in load
    module = import_module(match.group('module'))
  File "/usr/local/lib/python3.8/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1014, in _gcd_import
  File "<frozen importlib._bootstrap>", line 991, in _find_and_load
  File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 671, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 843, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/otel-auto-instrumentation/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py", line 26, in <module>
    from opentelemetry.exporter.otlp.proto.common.trace_encoder import (
  File "/otel-auto-instrumentation/opentelemetry/exporter/otlp/proto/common/trace_encoder.py", line 16, in <module>
    from opentelemetry.exporter.otlp.proto.common._internal.trace_encoder import (
  File "/otel-auto-instrumentation/opentelemetry/exporter/otlp/proto/common/_internal/__init__.py", line 21, in <module>
    from opentelemetry.proto.common.v1.common_pb2 import (
  File "/otel-auto-instrumentation/opentelemetry/proto/common/v1/common_pb2.py", line 5, in <module>
    from google.protobuf import descriptor as _descriptor
ModuleNotFoundError: No module named 'google.protobuf'
Failed to auto initialize opentelemetry
Traceback (most recent call last):
  File "/otel-auto-instrumentation/opentelemetry/instrumentation/auto_instrumentation/sitecustomize.py", line 121, in initialize
    _load_configurators()
  File "/otel-auto-instrumentation/opentelemetry/instrumentation/auto_instrumentation/sitecustomize.py", line 109, in _load_configurators
    raise exc
  File "/otel-auto-instrumentation/opentelemetry/instrumentation/auto_instrumentation/sitecustomize.py", line 105, in _load_configurators
    entry_point.load()().configure(auto_instrumentation_version=__version__)  # type: ignore
  File "/otel-auto-instrumentation/opentelemetry/sdk/_configuration/__init__.py", line 395, in configure
    self._configure(**kwargs)
  File "/otel-auto-instrumentation/opentelemetry/sdk/_configuration/__init__.py", line 411, in _configure
    _initialize_components(kwargs.get("auto_instrumentation_version"))
  File "/otel-auto-instrumentation/opentelemetry/sdk/_configuration/__init__.py", line 338, in _initialize_components
    trace_exporters, metric_exporters, log_exporters = _import_exporters(
  File "/otel-auto-instrumentation/opentelemetry/sdk/_configuration/__init__.py", line 259, in _import_exporters
    for (exporter_name, exporter_impl,) in _import_config_components(
  File "/otel-auto-instrumentation/opentelemetry/sdk/_configuration/__init__.py", line 101, in _import_config_components
    next(
  File "/otel-auto-instrumentation/importlib_metadata/__init__.py", line 208, in load
    module = import_module(match.group('module'))
  File "/usr/local/lib/python3.8/importlib/__init__.py", line 127, in import_module
    return _bootstrap._gcd_import(name[level:], package, level)
  File "<frozen importlib._bootstrap>", line 1014, in _gcd_import
  File "<frozen importlib._bootstrap>", line 991, in _find_and_load
  File "<frozen importlib._bootstrap>", line 975, in _find_and_load_unlocked
  File "<frozen importlib._bootstrap>", line 671, in _load_unlocked
  File "<frozen importlib._bootstrap_external>", line 843, in exec_module
  File "<frozen importlib._bootstrap>", line 219, in _call_with_frames_removed
  File "/otel-auto-instrumentation/opentelemetry/exporter/otlp/proto/http/trace_exporter/__init__.py", line 26, in <module>
    from opentelemetry.exporter.otlp.proto.common.trace_encoder import (
  File "/otel-auto-instrumentation/opentelemetry/exporter/otlp/proto/common/trace_encoder.py", line 16, in <module>
    from opentelemetry.exporter.otlp.proto.common._internal.trace_encoder import (
  File "/otel-auto-instrumentation/opentelemetry/exporter/otlp/proto/common/_internal/__init__.py", line 21, in <module>
    from opentelemetry.proto.common.v1.common_pb2 import (
  File "/otel-auto-instrumentation/opentelemetry/proto/common/v1/common_pb2.py", line 5, in <module>
    from google.protobuf import descriptor as _descriptor
ModuleNotFoundError: No module named 'google.protobuf'
import worked!

Additional context
I have been facing this issue while using the kubernetes opentelemetry operator.

This issue breaks the operator auto instrumentation whenever you have google.auth installed.

What the operator does when a pod is marked for auto-instrumentation is that it will use an init container with the image [ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-python:0.39b0](http://ghcr.io/open-telemetry/opentelemetry-operator/autoinstrumentation-python:0.39b0) (Dockerfile) and copy the data of the python environment that has been built in that image in /otel-auto-instrumentation (init container definition).

Now the problem is happening because for some reason the sitecustomize.py file can't import google.protobuf.
This is due to a wrongly cached entry in sys.modules referencing google.auth as being part of the system site-packages but not the otel-auto-instrumentation folder...

Please note that in the above traceback example the error is triggered in the sitecustomize.py, NOT in the actual python execution, the import google.protobuf actually succeeds otherwise you would see the stacktrace twice and not the printed message.

I have found 2 workarounds:

  1. Install protobuf in my environment, this removes the error and allows the auto-instrumentation to proceed but it defeats the purpose of the operator which promises a no code change instrumentation.
  2. Add site.addsitedir('/otel-auto-instrumentation') at the very beginning of the sitecustomize.py module, ideally in the initialize function. This fixes the issue has it seems that then Python handles the modules properly. The '/otel-auto-instrumentation' folder can obviously be inferred and doesn't have to be hardcoded.

I understand this seems to be related to that one package but I feel like this should be fixed here just in case other packages with namespaces behave the same.

If you agree with 2. I would gladly open a pull request to include the fix.

Here is a related issue where the problem actually happens the other way around: https://github.com/googleapis/google-auth-library-python/issues/269

Thanks for your time reading this quite long issue, hope it helps 😉

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the auto-instrumentation sitecustomize.py mentioned in the traceback, especially initialize and the configurator-loading path. Reproduce the Docker example with google-auth installed, then verify that auto-instrumentation loads google.protobuf without the configuration error while the import still succeeds.

Written by the indexing model from the issue text.

Assessment

Tech stack
python
Domain
observability-sre
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.