Skip to content

Commit 654e768

Browse files
feat(storage): add OpenTelemetry metrics gating and configuration module (#18407)
Introduce the foundational OpenTelemetry client metrics gating and configuration module for Google Cloud Storage. - Add `_opentelemetry_metrics.py` with a development gate (`_ENABLE_METRICS_DEV_GATE`) and environment variable parsing (`GCP_STORAGE_PYTHON_ENABLE_OTEL_METRICS`, `GCP_STORAGE_PYTHON_ENABLE_OTEL_DEBUG_METRICS`). - Implement `is_metrics_enabled()` and `is_advanced_metrics_enabled()` evaluation helpers with precedence hierarchy (dev gate -> client setting -> environment variable -> default). - Provide `get_common_attributes()` and `get_meter()` helpers for OpenTelemetry metrics instrumentation. - Add unit tests in `test__opentelemetry_metrics.py` covering gating resolution, environment variable overrides, `ImportError` fallback, and attribute helpers.
1 parent 181e26a commit 654e768

2 files changed

Lines changed: 469 additions & 0 deletions

File tree

Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Manages OpenTelemetry metrics instruments and gating for GCS client."""
16+
17+
import logging
18+
import os
19+
from typing import Any, Dict, Optional
20+
21+
from google.cloud.storage.version import __version__
22+
23+
logger = logging.getLogger(__name__)
24+
25+
# ---------------------------------------------------------------------------
26+
# 1. Hidden Development Gate
27+
# ---------------------------------------------------------------------------
28+
# Must remain False in production branches.
29+
# Only enabled during active development and test runs.
30+
_ENABLE_METRICS_DEV_GATE = False
31+
32+
# ---------------------------------------------------------------------------
33+
# 2. Standardized Configuration and Environment Variable Names
34+
# ---------------------------------------------------------------------------
35+
ENABLE_OTEL_METRICS_ENV_VAR = "GCP_STORAGE_PYTHON_ENABLE_OTEL_METRICS"
36+
ENABLE_OTEL_DEBUG_METRICS_ENV_VAR = "GCP_STORAGE_PYTHON_ENABLE_OTEL_DEBUG_METRICS"
37+
38+
_DEFAULT_ENABLE_METRICS = False
39+
_DEFAULT_ENABLE_DEBUG_METRICS = False
40+
41+
# ---------------------------------------------------------------------------
42+
# 3. Optional OpenTelemetry Dependency Check
43+
# ---------------------------------------------------------------------------
44+
try:
45+
from opentelemetry import metrics
46+
47+
HAS_OPENTELEMETRY_METRICS = True
48+
except ImportError:
49+
HAS_OPENTELEMETRY_METRICS = False
50+
logger.debug(
51+
"OpenTelemetry metrics package (opentelemetry-api >= 1.12.0) is not "
52+
"installed. GCS client metrics are disabled."
53+
)
54+
55+
56+
_TRUTHY_ENV_VALUES = {"1", "true", "yes", "on"}
57+
_FALSY_ENV_VALUES = {"0", "false", "no", "off"}
58+
59+
60+
def _parse_bool_env(name: str, default: bool = False) -> bool:
61+
"""Parses a boolean from an environment variable."""
62+
val = os.environ.get(name)
63+
if val is None:
64+
return default
65+
normalized = val.strip().lower()
66+
if not normalized:
67+
return default
68+
if normalized in _TRUTHY_ENV_VALUES:
69+
return True
70+
if normalized in _FALSY_ENV_VALUES:
71+
return False
72+
logger.warning(
73+
"Unrecognized boolean value %r for environment variable %s; using default %s.",
74+
val,
75+
name,
76+
default,
77+
)
78+
return default
79+
80+
81+
def is_metrics_enabled(enable_metrics: Optional[bool] = None) -> bool:
82+
"""Evaluates whether standard GCS metrics should be recorded.
83+
84+
Args:
85+
enable_metrics: Optional boolean configured on the client instance.
86+
Takes precedence over the environment variable if specified.
87+
88+
Returns:
89+
bool: True if metrics recording is enabled, False otherwise.
90+
"""
91+
if enable_metrics is not None and not isinstance(enable_metrics, bool):
92+
raise TypeError("enable_metrics must be a boolean or None.")
93+
94+
if not HAS_OPENTELEMETRY_METRICS:
95+
return False
96+
97+
if not _ENABLE_METRICS_DEV_GATE:
98+
return False
99+
100+
if enable_metrics is not None:
101+
return enable_metrics
102+
103+
return _parse_bool_env(ENABLE_OTEL_METRICS_ENV_VAR, _DEFAULT_ENABLE_METRICS)
104+
105+
106+
def is_debug_metrics_enabled(
107+
enable_debug_metrics: Optional[bool] = None,
108+
) -> bool:
109+
"""Evaluates whether high-frequency debug metrics should be recorded.
110+
111+
Args:
112+
enable_debug_metrics: Optional boolean configured on the client
113+
instance. Takes precedence over the environment variable if
114+
specified.
115+
116+
Returns:
117+
bool: True if debug metrics recording is enabled, False otherwise.
118+
"""
119+
if enable_debug_metrics is not None and not isinstance(enable_debug_metrics, bool):
120+
raise TypeError("enable_debug_metrics must be a boolean or None.")
121+
122+
if not HAS_OPENTELEMETRY_METRICS:
123+
return False
124+
125+
if not _ENABLE_METRICS_DEV_GATE:
126+
return False
127+
128+
if enable_debug_metrics is not None:
129+
return enable_debug_metrics
130+
131+
return _parse_bool_env(
132+
ENABLE_OTEL_DEBUG_METRICS_ENV_VAR, _DEFAULT_ENABLE_DEBUG_METRICS
133+
)
134+
135+
136+
# ---------------------------------------------------------------------------
137+
# 4. Standard Common Attributes & Meter Provider
138+
# ---------------------------------------------------------------------------
139+
_COMMON_ATTRIBUTES: Dict[str, Any] = {
140+
"gcp.client.service": "storage",
141+
"gcp.client.version": __version__,
142+
"gcp.client.repo": "googleapis/google-cloud-python",
143+
"gcp.client.artifact": "google-cloud-storage",
144+
}
145+
146+
147+
def get_common_attributes() -> Dict[str, Any]:
148+
"""Returns a copy of standard GCS client attributes for metrics."""
149+
return _COMMON_ATTRIBUTES.copy()
150+
151+
152+
def get_meter(
153+
enable_metrics: Optional[bool] = None,
154+
enable_debug_metrics: Optional[bool] = None,
155+
meter_provider: Optional[Any] = None,
156+
) -> Optional[Any]:
157+
"""Returns the OpenTelemetry Meter for Google Cloud Storage.
158+
159+
Args:
160+
enable_metrics: Optional boolean configured on the client instance for
161+
standard metrics.
162+
enable_debug_metrics: Optional boolean configured on the client
163+
instance for debug metrics.
164+
meter_provider: Optional custom OpenTelemetry MeterProvider instance.
165+
Defaults to the global MeterProvider if None.
166+
167+
Returns:
168+
Optional[Any]: The OpenTelemetry Meter instance if standard or debug
169+
metrics are enabled, or None otherwise.
170+
"""
171+
if not (
172+
is_metrics_enabled(enable_metrics)
173+
or is_debug_metrics_enabled(enable_debug_metrics)
174+
):
175+
return None
176+
return metrics.get_meter(
177+
"google.cloud.storage",
178+
__version__,
179+
meter_provider=meter_provider,
180+
)

0 commit comments

Comments
 (0)