Repository navigation
feat(storage): OpenTelemetry client metrics instrument registry and views - #14398
nidhiii-27 wants to merge 8 commits into
Conversation
…nfiguration - Add configuration options to StorageOptions, GrpcStorageOptions, and HttpStorageOptions (enableOtelMetrics, enableOtelDebugMetrics, meterProvider, metricInterval). - Add development gate checking system properties and environment variables in StorageMetricsConfig. - Implement package-private instrument registry in StorageClientMetrics. - Update OpenTelemetryBootstrappingUtils to register client views with custom histogram boundaries and create client meter provider. - Add comprehensive unit tests in StorageOptionsTest, StorageClientMetricsTest, and OpenTelemetryBootstrappingUtilsTest.
There was a problem hiding this comment.
Code Review
This pull request introduces OpenTelemetry client metrics support for Google Cloud Storage, allowing standard and debug metrics to be configured and recorded for both HTTP and gRPC transports. It adds configuration options to StorageOptions, GrpcStorageOptions, and HttpStorageOptions for enabling metrics, setting custom meter providers, and defining export intervals, supported by a new StorageMetricsConfig utility and StorageClientMetrics registry. Feedback on the changes suggests importing java.time.Duration to avoid using fully qualified class names throughout GrpcStorageOptions.java and OpenTelemetryBootstrappingUtils.java, which will improve code readability and consistency.
Apply spotify fmt-maven-plugin formatting to StorageMetricsConfig, StorageClientMetrics, StorageOptionsTest, and StorageClientMetricsTest. [Generated-by: AI]
Use simple class name Duration instead of fully qualified java.time.Duration in GrpcStorageOptions and OpenTelemetryBootstrappingUtils. [Generated-by: AI]
…trappingUtils Apply spotify fmt-maven-plugin formatting to OpenTelemetryBootstrappingUtils. [Generated-by: AI]
…nd views [Generated-by: AI]
… metrics Refactor addClientHistogramView in OpenTelemetryBootstrappingUtils to remove unit selector and custom view description, preserving instrument default descriptions. Remove deferred Phase 2 connection metrics (dnsLookupDuration, tcpConnectDuration, tlsHandshakeDuration) from StorageClientMetrics, CLIENT_LATENCY_HISTOGRAMS, and StorageClientMetricsTest. Ensure clean Duration imports. [Generated-by: AI]
…rProvider Default api parameter to "grpc" instead of "storage" in createClientMeterProvider, allow specifying "json" / "http" transport protocols for Cloud Monitoring monitored resource storage.googleapis.com/Client, and add unit test coverage in OpenTelemetryBootstrappingUtilsTest. [Generated-by: AI]
There was a problem hiding this comment.
Requesting changes mainly for the metric types/names and for accuracy of the recorded data (see inline comments).
One question on the overall design: createMeterProvider is only reached from enableGrpcMetrics(). Please state in the PR description how metrics are exported for HttpStorageOptions when metrics are enabled and no MeterProvider is supplied; both transports are expected to export through the same SDK-owned provider.
cpriti-os
left a comment
There was a problem hiding this comment.
Thanks for addressing the previous comments! Left a few small updates from our latest metrics/attributes alignment.
| static final AttributeKey<String> KEY_GCP_CLIENT_ARTIFACT = | ||
| AttributeKey.stringKey("gcp.client.artifact"); | ||
| static final AttributeKey<String> KEY_GCP_CLIENT_INSTANCE_ID = | ||
| AttributeKey.stringKey("gcp.client.instance_id"); |
There was a problem hiding this comment.
Could we also define the AttributeKey constants for the metric attributes here so the instrumentation PRs can share them? Specifically: gcp.storage.operation, gcp.client.method, rpc.method, rpc.system.name, rpc.response.status_code (string status name, replacing rpc.grpc.status_code), http.request.method, http.response.status_code, server.address, server.port, error.type, network.path ("directpath", "cloudpath", "unknown"), and network.io.direction ("receive", "transmit").
| static final String METRIC_GCP_STORAGE_CLIENT_OPERATIONS = "gcp.storage.client.operations"; | ||
| static final String METRIC_GCP_STORAGE_CLIENT_ATTEMPTS = "gcp.storage.client.attempts"; | ||
| static final String METRIC_GCP_STORAGE_CLIENT_ERRORS = "gcp.storage.client.errors"; | ||
| static final String METRIC_GCP_STORAGE_CLIENT_OPERATION_TTFB = |
There was a problem hiding this comment.
We added a 10th standard metric gcp.storage.client.retries (LongCounter, unit "1", description "Number of GCS client retry attempts."). Could you add METRIC_GCP_STORAGE_CLIENT_RETRIES here as well?
| static final String METRIC_GCP_STORAGE_CLIENT_ATTEMPTS = "gcp.storage.client.attempts"; | ||
| static final String METRIC_GCP_STORAGE_CLIENT_ERRORS = "gcp.storage.client.errors"; | ||
| static final String METRIC_GCP_STORAGE_CLIENT_OPERATION_TTFB = | ||
| "gcp.storage.client.operation.ttfb"; |
There was a problem hiding this comment.
Please rename gcp.storage.client.operation.ttfb to gcp.storage.client.ttfb (METRIC_GCP_STORAGE_CLIENT_TTFB, getTtfb()) here and in CLIENT_LATENCY_HISTOGRAMS to match Go and the Monarch descriptor (since TTFB is per-attempt, not per-operation).
| meter | ||
| .histogramBuilder(METRIC_RPC_CLIENT_CALL_DURATION) | ||
| .setDescription("Duration of one gRPC request. Retries not included (Otel)") | ||
| .setUnit("s") |
There was a problem hiding this comment.
nit: Can we drop "(Otel)" from the rpc.client.call.duration and http.client.request.duration descriptions (e.g. "Duration of a single gRPC client call attempt to Cloud Storage." / "Duration of a single HTTP request attempt to Cloud Storage." )?
| private final Attributes clientAttributes; | ||
|
|
||
| static StorageClientMetrics create(MeterProvider meterProvider, boolean enableOtelDebugMetrics) { | ||
| return create(meterProvider, UUID.randomUUID().toString(), enableOtelDebugMetrics); |
There was a problem hiding this comment.
enableOtelMetrics and enableOtelDebugMetrics should work independently (so setting enableOtelMetrics = false and enableOtelDebugMetrics = true initializes only the debug instruments and leaves the standard instruments null). Could we pass both boolean enableOtelMetrics, boolean enableOtelDebugMetrics into StorageClientMetrics.create(...) and gate the standard instruments on enableOtelMetrics?
This PR implements Phase 1 (Core Metrics Registry & Views) of the OpenTelemetry Client Metrics feature in the Java Cloud Storage SDK (
google-cloud-storage).Following review feedback and architectural alignment with other Storage SDKs, this PR focuses strictly on the package-private instrument registry and OpenTelemetry view/histogram boundary bootstrapping, decoupling it from public options and configuration APIs (split into companion follow-up PR #14484).
Key Changes
Instrument Registry (
StorageClientMetrics)rpc.client.call.duration(s)http.client.request.duration(s)gcp.client.request.duration(s)gcp.storage.client.operations(1)gcp.storage.client.attempts(1)gcp.storage.client.errors(1)gcp.storage.client.operation.ttfb(s)gcp.storage.client.request.body.size(By)gcp.storage.client.response.body.size(By)gcp.storage.client.request.active(1)gcp.storage.client.gfe.duration(s)gcp.storage.client.gfe.header_missing(1)gcp.storage.client.stall.duration(s)gcp.storage.client.network.bytes.sent(By)gcp.storage.client.network.bytes.received(By)gcp.storage.client.auth.credential_refresh.duration(s)dns.lookup.duration,tcp.connect.duration,tls.handshake.duration) as deferred placeholders.OpenTelemetry Bootstrapping (
OpenTelemetryBootstrappingUtils)CLIENT_LATENCY_HISTOGRAMSusing 2ms-resolution exponential buckets.CLIENT_SIZE_HISTOGRAMSusing 128KiB to 16GiB buckets.createClientMeterProviderwithstorage.googleapis.com/Clientmonitored resource.Testing
opentelemetry-sdk-testingtopom.xml.StorageClientMetricsTestverifying instrument registration and custom view boundaries viaInMemoryMetricReader.OpenTelemetryBootstrappingUtilsTest.[Generated-by: AI]