Skip to content

feat(observability): [WIP] implement universal 4-path OpenTelemetry tracing - #18433

Draft
chalmerlowe wants to merge 95 commits into
mainfrom
feat/otel-tracing-universal-4path
Draft

chalmerlowe wants to merge 95 commits into
mainfrom
feat/otel-tracing-universal-4path

Conversation

@chalmerlowe

Copy link
Copy Markdown
Contributor

feat(observability): implement universal 4-path OpenTelemetry tracing

Problems Solved

Google Cloud Python client libraries support four communication paths: synchronous gRPC, asynchronous gRPC, synchronous REST (HTTP), and asynchronous REST (HTTP). Previously, distributed OpenTelemetry tracing was only wired for synchronous gRPC calls, leaving asynchronous and HTTP communications untraced. Additionally, earlier drafts of asynchronous tracing attempted to modify gRPC channels after creation, which violated the immutability rules of the underlying Python gRPC library, and did not consistently forward client options across all transport classes.

Solutions

This pull request provides a unified, cross-transport tracing implementation:

  1. Universal 4-Transport Support:

    • Synchronous gRPC (GrpcTransport): Continues using OpenTelemetry gRPC channel interceptors.
    • Asynchronous gRPC (GrpcAsyncIOTransport): Supplies OpenTelemetry interceptors directly during channel creation, respecting the immutable design of asynchronous gRPC channels.
    • Synchronous REST (RestTransport): Adds wire span tracking around HTTP requests with automatic W3C trace context header injection (traceparent).
    • Asynchronous REST (AsyncRestTransport): Integrates HTTP wire span tracking and async context lifecycle handling with W3C header propagation.
  2. Refined Transport Contracts & Cleanup:

    • Replaced per-instance signature inspection with module-level constants to eliminate overhead during method invocation.
    • Configured BaseTransport.kind to safely return an empty string by default instead of raising an exception.
    • Updated REST and base transport initializers to accept and store client options, ensuring options are forwarded uniformly across all transport classes.
    • Resolved an issue in asynchronous method wrappers where caller-specified compression settings were not correctly evaluated.
    • Added task cancellation handling (asyncio.CancelledError) so spans are closed accurately when asynchronous tasks are cancelled.

Notes for Reviewers

  • This pull request targets template definitions in packages/gapic-generator and core helper functions in packages/google-api-core.
  • Per team coordination, generated client library code and integration goldens are not included in this change and will be updated in a follow-up step.
  • All additions maintain backward compatibility and preserve zero overhead when tracing is disabled.

… hook

- Add rpc.system.name: 'grpc'
- Extract server.address and server.port from client options endpoint
- Extract gcp.grpc.resend_count from request resend count
- Extract gcp.resource.destination.id from request name or parent
- Add _client_response_hook for status code, error.type, and status.message
- Plumb response_hook into get_otel_interceptor and get_otel_async_interceptor
- Test endpoint attribute parsing across host/port variations
- Test destination id and resend count extraction
- Test client request and response hooks covering all status and error cases
- Test interceptor creation and custom endpoint attribute propagation
- Achieve 100% statement and branch coverage on _observability.py
…and hooks

- Rename _extract_t4_attributes to _extract_grpc_request_attributes
- Rename _make_client_request_hook to _make_grpc_client_request_hook
- Rename _client_request_hook to _grpc_client_request_hook
- Rename _client_response_hook to _grpc_client_response_hook
- Preserve generic _extract_endpoint_attributes for shared transport usage
…ntion

- Rename test_extract_t4_attributes to test_extract_grpc_request_attributes
- Rename test_client_request_hook to test_grpc_client_request_hook
- Rename test_client_response_hook to test_grpc_client_response_hook
- Update interceptor hook references to _grpc_client_* hooks
- Add url.domain extraction from universe_domain or default to googleapis.com
- Add _extract_error_attributes helper to extract gcp.errors.domain and gcp.errors.metadata.<key>
- Omit server.port when port matches scheme defaults (443 for https/grpc, 80 for http)
- Remove redundant _grpc_client_response_hook and _STATUS_CODE_NAMES
- Deduplicate name and parent resource lookup for gcp.resource.destination.id
- Add comprehensive parametrized unit tests and update interceptor test suites
…tem attribute

- Strip leading slash from gRPC attempt span names via span.update_name
- Set rpc.method to the fully qualified method name per PRD specification
- Retain rpc.system.name: 'grpc' and remove legacy rpc.system attribute to avoid duplication
- Update unit tests to verify span name normalization and attribute deduplication
- Remove gcp.resource.destination.id extraction from _extract_grpc_request_attributes
- Update unit tests to reflect attribute removal per July Strategy Update
- Broaden transport check in client.py.j2 to allow gRPC transport subclasses.
- Align version comments in client.py.j2 and grpc.py.j2 to 2.36.0+.
- Synchronize all golden client and transport files with template updates.
- Harden zero-overhead and custom tracer provider isolation assertions in test_tracing.py.
- Add direct client initialization test to verify template injection end-to-end.
…port template

- Place ClientInterceptor import under if TYPE_CHECKING: in grpc.py.j2 to eliminate runtime import overhead and avoid import failures on older google-api-core versions.
- String-quote "ClientInterceptor" in the interceptors type annotation for GrpcTransport.__init__.
- Regenerate and synchronize all golden gRPC transport files.
Align if TYPE_CHECKING: in golden gRPC transport files with # pragma: NO COVER to match grpc.py.j2 template output.
… branch

- Drop # pragma: NO COVER annotations from lines 181-183 in base.py.j2.
- Remove outdated comment since unit tests explicitly exercise this fallback.
…d symbol patching

- Parametrize client options and channel injection tests in gapic-generator template and test_method_async
- Replace string-based module patching with refactor-safe symbol patching
- Add boundary tests for client options and fail-open mutation test for telemetry initialization
- Regenerate Bazel integration goldens to match updated unit test template
- Safeguard trace_http_request context manager exit when initialization fails open

This branch has not been deployed

No deployments
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.

2 participants