Skip to content
Open
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,7 @@
import io.grpc.StatusRuntimeException;
import io.grpc.protobuf.ProtoUtils;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.metrics.MeterProvider;
import java.io.Closeable;
import java.io.IOException;
import java.io.InputStream;
Expand All @@ -122,6 +123,7 @@
import java.util.concurrent.ScheduledExecutorService;
import java.util.logging.Logger;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
* @since 2.14.0
Expand Down Expand Up @@ -432,6 +434,10 @@ public int hashCode() {
grpcInterceptorProvider,
blobWriteSessionConfig,
openTelemetry,
enableOtelMetrics,
enableOtelDebugMetrics,
getMeterProvider(),
getMetricInterval(),
baseHashCode());
}

Expand All @@ -446,11 +452,15 @@ public boolean equals(Object o) {
GrpcStorageOptions that = (GrpcStorageOptions) o;
return attemptDirectPath == that.attemptDirectPath
&& enableGrpcClientMetrics == that.enableGrpcClientMetrics
&& enableOtelMetrics == that.enableOtelMetrics
&& enableOtelDebugMetrics == that.enableOtelDebugMetrics
&& Objects.equals(retryAlgorithmManager, that.retryAlgorithmManager)
&& Objects.equals(terminationAwaitDuration, that.terminationAwaitDuration)
&& Objects.equals(grpcInterceptorProvider, that.grpcInterceptorProvider)
&& Objects.equals(blobWriteSessionConfig, that.blobWriteSessionConfig)
&& Objects.equals(openTelemetry, that.openTelemetry)
&& Objects.equals(getMeterProvider(), that.getMeterProvider())
&& Objects.equals(getMetricInterval(), that.getMetricInterval())
&& this.baseEquals(that);
}

Expand Down Expand Up @@ -747,6 +757,64 @@ public GrpcStorageOptions.Builder setApiTracerFactory(ApiTracerFactory apiTracer
public GrpcStorageOptions.Builder setOpenTelemetry(OpenTelemetry openTelemetry) {
requireNonNull(openTelemetry, "openTelemetry must be non null");
this.openTelemetry = openTelemetry;
this.customOpenTelemetryConfigured = true;
return this;
}

/**
* Enable or disable OpenTelemetry client metrics.
*
* @param enableOtelMetrics whether OpenTelemetry client metrics should be enabled
* @since 2.74.0 This new api is in preview and is subject to breaking changes.
*/
@BetaApi
@Override
public GrpcStorageOptions.Builder setEnableOtelMetrics(boolean enableOtelMetrics) {
super.setEnableOtelMetrics(enableOtelMetrics);
return this;
}

/**
* Enable or disable OpenTelemetry debug client metrics.
*
* @param enableOtelDebugMetrics whether OpenTelemetry debug client metrics should be enabled
* @since 2.74.0 This new api is in preview and is subject to breaking changes.
*/
@BetaApi
@Override
public GrpcStorageOptions.Builder setEnableOtelDebugMetrics(boolean enableOtelDebugMetrics) {
super.setEnableOtelDebugMetrics(enableOtelDebugMetrics);
return this;
}

/**
* Set a custom {@link MeterProvider} for recording client metrics.
*
* @param meterProvider custom MeterProvider to use, or null to use the default
* @since 2.74.0 This new api is in preview and is subject to breaking changes.
*/
@BetaApi
@Override
public GrpcStorageOptions.Builder setMeterProvider(@Nullable MeterProvider meterProvider) {
super.setMeterProvider(meterProvider);
return this;
}

/**
* Set the metric export interval for periodic metric reading.
*
* <p>When using the default Cloud Monitoring exporter, the interval must be at least 60 seconds
* (1 minute). Intervals less than 60 seconds are permitted only when a custom {@link
* MeterProvider} is configured via {@link #setMeterProvider(MeterProvider)} or explicit
* OpenTelemetry is configured via {@link #setOpenTelemetry(OpenTelemetry)}.
*
* @param metricInterval interval duration
* @since 2.74.0 This new api is in preview and is subject to breaking changes.
*/
@BetaApi
@Override
public GrpcStorageOptions.Builder setMetricInterval(Duration metricInterval) {
Comment thread
nidhiii-27 marked this conversation as resolved.
super.setMetricInterval(metricInterval);
return this;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,16 @@
import com.google.common.base.MoreObjects;
import com.google.common.collect.ImmutableSet;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.metrics.MeterProvider;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.time.Clock;
import java.time.Duration;
import java.util.Objects;
import java.util.Set;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.checker.nullness.qual.Nullable;

/**
* @since 2.14.0
Expand Down Expand Up @@ -110,7 +113,14 @@ public HttpStorageOptions.Builder toBuilder() {
@Override
public int hashCode() {
return Objects.hash(
retryAlgorithmManager, blobWriteSessionConfig, openTelemetry, baseHashCode());
retryAlgorithmManager,
blobWriteSessionConfig,
openTelemetry,
enableOtelMetrics,
enableOtelDebugMetrics,
getMeterProvider(),
getMetricInterval(),
baseHashCode());
}

@Override
Expand All @@ -125,6 +135,10 @@ public boolean equals(Object o) {
return Objects.equals(retryAlgorithmManager, that.retryAlgorithmManager)
&& Objects.equals(blobWriteSessionConfig, that.blobWriteSessionConfig)
&& Objects.equals(openTelemetry, that.openTelemetry)
&& enableOtelMetrics == that.enableOtelMetrics
&& enableOtelDebugMetrics == that.enableOtelDebugMetrics
&& Objects.equals(getMeterProvider(), that.getMeterProvider())
&& Objects.equals(getMetricInterval(), that.getMetricInterval())
&& this.baseEquals(that);
}

Expand Down Expand Up @@ -315,6 +329,64 @@ public HttpStorageOptions build() {
public HttpStorageOptions.Builder setOpenTelemetry(OpenTelemetry openTelemetry) {
requireNonNull(openTelemetry, "openTelemetry must be non null");
this.openTelemetry = openTelemetry;
this.customOpenTelemetryConfigured = true;
return this;
}

/**
* Enable or disable OpenTelemetry client metrics.
*
* @param enableOtelMetrics whether OpenTelemetry client metrics should be enabled
* @since 2.74.0 This new api is in preview and is subject to breaking changes.
*/
@BetaApi
@Override
public HttpStorageOptions.Builder setEnableOtelMetrics(boolean enableOtelMetrics) {
super.setEnableOtelMetrics(enableOtelMetrics);
return this;
}

/**
* Enable or disable OpenTelemetry debug client metrics.
*
* @param enableOtelDebugMetrics whether OpenTelemetry debug client metrics should be enabled
* @since 2.74.0 This new api is in preview and is subject to breaking changes.
*/
@BetaApi
@Override
public HttpStorageOptions.Builder setEnableOtelDebugMetrics(boolean enableOtelDebugMetrics) {
super.setEnableOtelDebugMetrics(enableOtelDebugMetrics);
return this;
}

/**
* Set a custom {@link MeterProvider} for recording client metrics.
*
* @param meterProvider custom MeterProvider to use, or null to use the default
* @since 2.74.0 This new api is in preview and is subject to breaking changes.
*/
@BetaApi
@Override
public HttpStorageOptions.Builder setMeterProvider(@Nullable MeterProvider meterProvider) {
super.setMeterProvider(meterProvider);
return this;
}

/**
* Set the metric export interval for periodic metric reading.
*
* <p>When using the default Cloud Monitoring exporter, the interval must be at least 60 seconds
* (1 minute). Intervals less than 60 seconds are permitted only when a custom {@link
* MeterProvider} is configured via {@link #setMeterProvider(MeterProvider)} or explicit
* OpenTelemetry is configured via {@link #setOpenTelemetry(OpenTelemetry)}.
*
* @param metricInterval interval duration
* @since 2.74.0 This new api is in preview and is subject to breaking changes.
*/
@BetaApi
@Override
public HttpStorageOptions.Builder setMetricInterval(Duration metricInterval) {
super.setMetricInterval(metricInterval);
return this;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright 2026 Google LLC
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package com.google.cloud.storage;

import com.google.common.annotations.VisibleForTesting;
import java.util.Locale;
import java.util.function.Function;
import java.util.logging.Logger;
import org.checkerframework.checker.nullness.qual.Nullable;

final class StorageMetricsConfig {
private static final Logger LOGGER = Logger.getLogger(StorageMetricsConfig.class.getName());

static final String ENV_ENABLE_OTEL_METRICS_JAVA = "GCP_STORAGE_JAVA_ENABLE_OTEL_METRICS";
static final String ENV_ENABLE_OTEL_DEBUG_METRICS = "GCP_STORAGE_JAVA_ENABLE_OTEL_DEBUG_METRICS";

private static volatile Function<String, String> envResolver = System::getenv;

private StorageMetricsConfig() {}

static boolean isEnableOtelMetrics() {
return isEnableOtelMetrics(null);
}

static boolean isEnableOtelDebugMetrics() {
return isEnableOtelDebugMetrics(null);
}

static boolean isEnableOtelMetrics(@Nullable Boolean builderSetting) {
return isEnableOtelMetrics(envResolver, builderSetting);
}

static boolean isEnableOtelDebugMetrics(@Nullable Boolean builderSetting) {
return isEnableOtelDebugMetrics(envResolver, builderSetting);
}

@VisibleForTesting
Comment thread
nidhiii-27 marked this conversation as resolved.
static boolean isEnableOtelMetrics(
Function<String, String> env, @Nullable Boolean builderSetting) {
return resolveFlag(env, builderSetting, ENV_ENABLE_OTEL_METRICS_JAVA);
}

@VisibleForTesting
static boolean isEnableOtelDebugMetrics(
Function<String, String> env, @Nullable Boolean builderSetting) {
return resolveFlag(env, builderSetting, ENV_ENABLE_OTEL_DEBUG_METRICS);
}

private static boolean resolveFlag(
Function<String, String> env, @Nullable Boolean builderSetting, String envVarName) {
if (builderSetting != null) {
return builderSetting;
}
String javaEnv = env.apply(envVarName);
Boolean parsed = parseBooleanValue(javaEnv, envVarName);
if (parsed != null) {
return parsed;
}
return false;
}

@Nullable
@VisibleForTesting
static Boolean parseBooleanValue(@Nullable String val, String varName) {
if (val == null) {
return null;
}
String normalized = val.trim().toLowerCase(Locale.ENGLISH);
switch (normalized) {
case "1":
case "t":
case "true":
return Boolean.TRUE;
case "0":
case "f":
case "false":
return Boolean.FALSE;
default:
LOGGER.warning(
"Unrecognized boolean value '"
+ val
+ "' for "
+ varName
+ "; ignoring and falling back to builder/default");
return null;
}
}

@VisibleForTesting
static void setEnvResolverForTesting(Function<String, String> testEnv) {
envResolver = testEnv != null ? testEnv : System::getenv;
}

@VisibleForTesting
static void resetResolversForTesting() {
envResolver = System::getenv;
}
}
Loading
Loading