Skip to content

feat(storage): add OpenTelemetry standard metrics for Async gRPC client - #18762

Draft
shradhakatyal wants to merge 2 commits into
googleapis:mainfrom
shradhakatyal:feat/gcs-otel-grpc-standard-metrics
Draft

shradhakatyal wants to merge 2 commits into
googleapis:mainfrom
shradhakatyal:feat/gcs-otel-grpc-standard-metrics

Conversation

@shradhakatyal

Copy link
Copy Markdown
Contributor

Summary

Implements OpenTelemetry Standard Metrics instrumentation for the Async gRPC client (AsyncGrpcClient) and Bidi streaming abstractions in google-cloud-storage:

  • Core Metrics Infrastructure (_opentelemetry_metrics.py):
    • Defines all 9 OpenTelemetry standard metric instruments (gcp.storage.operation.count, gcp.storage.operation.duration, gcp.storage.operation.retry.count, gcp.storage.operation.transfer_size, gcp.storage.api.request.count, gcp.storage.api.request.duration, gcp.storage.api.request.body_size, gcp.storage.api.response.body_size, gcp.storage.stream.idle_duration), histogram bucket views, and attribute keys.
    • Adds gRPC transport endpoint parsing (extract_grpc_server_address_and_network_path), gRPC status/error classification (extract_grpc_status_code, classify_error_type), and Bidi stream metrics (_GrpcBidiStreamMetricsContext).
  • AsyncGrpcClient (async_grpc_client.py):
    • Initializes _metrics_manager in AsyncGrpcClient.__init__ using the configured gRPC transport endpoint and user options (enable_metrics, meter_provider, metrics_exporter).
    • Wraps unary operations (delete_object, get_object_metadata, compose_object, restore_object, move_object, update_object_metadata, lock_bucket_retention_policy, start_resumable_write, query_write_status) with start_operation and start_api_request, wiring GAPIC AsyncRetry(on_error=...) callbacks to record retry counts and per-attempt API request metrics.
    • Wraps download_object (server-streaming ReadObject) with operation and API request contexts, recording record_response_bytes per chunk.
    • Wraps upload_object (BidiWriteObject) with operation and Bidi stream metrics, recording record_request_bytes and stream.idle_duration.
  • Bidi Streaming Abstractions (async_multi_range_downloader.py, async_appendable_object_writer.py, async_read_object_stream.py, async_write_object_stream.py, _stream_multiplexer.py):
    • Instruments AsyncMultiRangeDownloader (open, download_ranges, _reconnect), AsyncAppendableObjectWriter (open, append, flush, close, simple_flush, _reconnect_and_recover), AsyncReadObjectStream, AsyncWriteObjectStream, and _StreamMultiplexer with operation, Bidi stream, transfer size, retry, and idle duration metrics.
  • Unit Tests:
    • Comprehensive unit tests across test__opentelemetry_metrics.py and tests/unit/asyncio/ (348 unit tests passing).

[Generated-by: AI]

…d helpers

Implement core OpenTelemetry standard metrics infrastructure for GCS:
- Add _MetricsRecorder with the 9 standard metric instruments
- Add custom latency (165 buckets) and byte-size (45 buckets) histogram views
- Add classify_error_type for low-cardinality error.type mapping
- Add normalize_url_template for bounded HTTP url.template routing
- Add start_operation ContextVar for outermost logical operation tracking and nested call suppression

[Generated-by: AI]
Implement OpenTelemetry standard metrics for AsyncGrpcClient, AsyncMultiRangeDownloader,
AsyncAppendableObjectWriter, AsyncReadObjectStream, AsyncWriteObjectStream, and
_StreamMultiplexer following the GCS O11y specification.

[Generated-by: AI]

@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 implements OpenTelemetry metrics support for the Google Cloud Storage asyncio client using gRPC. It introduces standard metric instruments, custom histogram bucket boundaries, canonical error classification, and route normalization. Additionally, it adds gRPC client interceptors and call tracking proxies to record physical attempt metrics, and integrates logical operation tracking across various asyncio stream and writer classes. The feedback suggests implementing __getattr__ in _TrackedCallBase to delegate unhandled attribute access to the underlying call object, ensuring robust compatibility with gRPC's internal implementation details.

)
self._network_path = network_path
self._recorded = False
self._closing_cleanly = False

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

To ensure full compatibility with gRPC's internal implementation details, other interceptors, or future gRPC updates that might access private or undocumented attributes on the call object, implement __getattr__ in _TrackedCallBase to transparently delegate any unhandled attribute or method access to the underlying self._call object.

        self._closing_cleanly = False

    def __getattr__(self, name: str) -> Any:
        return getattr(self._call, name)

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.

1 participant