Skip to content

feat(api-core): lay groundwork for stock OpenTelemetry gRPC tracing - #18026

Draft
chalmerlowe wants to merge 16 commits into
mainfrom
feat/otel-tracing-grpc-stock-interceptor
Draft

feat(api-core): lay groundwork for stock OpenTelemetry gRPC tracing#18026
chalmerlowe wants to merge 16 commits into
mainfrom
feat/otel-tracing-grpc-stock-interceptor

Conversation

@chalmerlowe

@chalmerlowe chalmerlowe commented Aug 7, 2026

Copy link
Copy Markdown
Contributor

Problem

We are integrating observability (o11y) tracing support into the Google API Core library. The goal for this phase is to allow automatic tracing of gRPC calls when the user has enabled it and installed the required OpenTelemetry packages. We need to ensure the system fails open (does not crash) if the OpenTelemetry packages are missing.

Solution

This Pull Request lays the groundwork by:

  1. Adding opentelemetry-api as a hard dependency.
  2. Adding opentelemetry-instrumentation-grpc as a soft dependency.
  3. Adding failing tests that verify the expected behavior of create_channel when OpenTelemetry is:
  • installed and enabled
  • installed but disabled
  • not installed but enabled
  • not installed and disabled

Notes to Reviewers

This is a work in progress Pull Request focusing exclusively on gRPC support and o11y infrastructure. We will be building on this foundation in future PRs.

The tests are expected to fail at this stage because the actual implementation of the channel wrapping logic has not been added yet. This Pull Request is being pushed to show progress and align on the testing strategy.

No features are fully implemented yet.

Refactored OtelSpanEnricher to OtelUnaryClientInterceptor to act as a span creator (Pure API approach). Integrated feature gating helpers to control tracing. Added dynamic attribute extraction from gRPC metadata. Cleaned up dead code and added samples.
Removed Translate samples as we are shifting focus to Secret Manager and avoiding auto-instrumentation. Fixed a TypeError in pytest.skip usage in test_tracing.py.
Added tests for __init__.py import failures and expanded metadata parsing tests in test_tracing.py to cover all branches and error handling.

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request introduces OpenTelemetry integration for gRPC channels in google-api-core, adding opentelemetry-api as a dependency and implementing unit tests to verify the behavior when tracing is enabled, disabled, or when OpenTelemetry is not installed. The review feedback focuses on improving the robustness of the unit tests by replacing the custom clean_sys_modules fixture with pytest's built-in monkeypatch to safely manage sys.modules modifications, and properly simulating a missing package by mapping its module to None instead of deleting it.

Comment on lines +25 to +39
@pytest.fixture
def clean_sys_modules():
"""Fixture to ensure opentelemetry modules are unloaded before and after tests."""
modules_to_remove = [
"opentelemetry.instrumentation.grpc",
"opentelemetry.instrumentation",
"opentelemetry",
]
for mod in modules_to_remove:
if mod in sys.modules:
del sys.modules[mod]
yield
for mod in modules_to_remove:
if mod in sys.modules:
del sys.modules[mod]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Instead of using a custom fixture clean_sys_modules that manually deletes keys from sys.modules (which can permanently remove modules imported by other tests and cause side effects), we can leverage pytest's built-in monkeypatch fixture. Using monkeypatch.setitem(sys.modules, ...) automatically restores the original state of sys.modules after each test runs, making the tests isolated and robust.

del sys.modules[mod]


def test_create_channel_otel_installed_and_enabled(monkeypatch, clean_sys_modules):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Remove the clean_sys_modules fixture from the test signature since we can use monkeypatch to safely manage sys.modules changes.

Suggested change
def test_create_channel_otel_installed_and_enabled(monkeypatch, clean_sys_modules):
def test_create_channel_otel_installed_and_enabled(monkeypatch):

mock_otel_grpc.client_interceptor.return_value = mock_interceptor
mock_otel_grpc.intercept_channel.side_effect = lambda ch, inc: f"wrapped_{ch}"

sys.modules["opentelemetry.instrumentation.grpc"] = mock_otel_grpc

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Use monkeypatch.setitem to mock the module in sys.modules. This ensures that the modification is automatically reverted after the test completes, preventing side effects on other tests.

Suggested change
sys.modules["opentelemetry.instrumentation.grpc"] = mock_otel_grpc
monkeypatch.setitem(sys.modules, "opentelemetry.instrumentation.grpc", mock_otel_grpc)

assert channel == f"wrapped_{mock_channel}"


def test_create_channel_otel_installed_but_disabled(monkeypatch, clean_sys_modules):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Remove the clean_sys_modules fixture from the test signature.

Suggested change
def test_create_channel_otel_installed_but_disabled(monkeypatch, clean_sys_modules):
def test_create_channel_otel_installed_but_disabled(monkeypatch):

"""Verify that create_channel does NOT wrap the channel if tracing is disabled."""

mock_otel_grpc = mock.Mock()
sys.modules["opentelemetry.instrumentation.grpc"] = mock_otel_grpc

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Use monkeypatch.setitem to mock the module in sys.modules to ensure automatic cleanup after the test runs.

Suggested change
sys.modules["opentelemetry.instrumentation.grpc"] = mock_otel_grpc
monkeypatch.setitem(sys.modules, "opentelemetry.instrumentation.grpc", mock_otel_grpc)

assert channel == mock_channel


def test_create_channel_otel_not_installed_fails_open(monkeypatch, clean_sys_modules):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Remove the clean_sys_modules fixture from the test signature.

Suggested change
def test_create_channel_otel_not_installed_fails_open(monkeypatch, clean_sys_modules):
def test_create_channel_otel_not_installed_fails_open(monkeypatch):

Comment on lines +101 to +103
# Ensure it's not in sys.modules
if "opentelemetry.instrumentation.grpc" in sys.modules:
del sys.modules["opentelemetry.instrumentation.grpc"]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Simply deleting "opentelemetry.instrumentation.grpc" from sys.modules is not sufficient to simulate the package not being installed. If the package is installed in the test environment, Python's import system will find it on sys.path and load it.

To reliably simulate a package not being installed, map its name to None in sys.modules. Combined with monkeypatch.setitem, this is both robust and safe.

    # Simulate OTel not being installed by mapping it to None
    monkeypatch.setitem(sys.modules, "opentelemetry.instrumentation.grpc", None)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant