Skip to content

Commit 9035de1

Browse files
committed
chore: refactor logic for Otel views
1 parent c982e17 commit 9035de1

8 files changed

Lines changed: 40 additions & 23 deletions

File tree

java-datastore/google-cloud-datastore/src/main/java/com/google/cloud/datastore/DatastoreImpl.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ public void close() throws Exception {
179179
} catch (Exception e) {
180180
logger.log(Level.WARNING, "Failed to close channels", e);
181181
}
182-
// Close the default Metrics Recorder if exists
182+
// Flush and shut down the built-in OTel SDK if active; no-op for user-provided instances.
183183
getOptions().getMetricsRecorder().close();
184184
}
185185

java-datastore/google-cloud-datastore/src/main/java/com/google/cloud/datastore/telemetry/BuiltInDatastoreMetricsProvider.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,9 @@ public final class BuiltInDatastoreMetricsProvider {
5555
private static final Logger logger =
5656
Logger.getLogger(BuiltInDatastoreMetricsProvider.class.getName());
5757

58+
// volatile ensures that writes from one thread (first call to detectClientLocation) are
59+
// immediately visible to all other threads sharing this singleton INSTANCE. Without it, a
60+
// thread could read a stale null and re-enter the initialization branch.
5861
private static volatile String location;
5962
private static final String DEFAULT_LOCATION = "global";
6063

java-datastore/google-cloud-datastore/src/main/java/com/google/cloud/datastore/telemetry/DatastoreBuiltInMetricsView.java

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,10 @@ private DatastoreBuiltInMetricsView() {}
7474
* @param metricExporter the exporter to use for metrics.
7575
* @param builder the builder to register views and the {@link PeriodicMetricReader} on.
7676
*/
77+
// Views are scoped to the SdkMeterProvider they are registered on. This method is called
78+
// exclusively from BuiltInDatastoreMetricsProvider, which constructs a private SdkMeterProvider
79+
// for the built-in Cloud Monitoring export path. A user-provided OpenTelemetry instance has its
80+
// own independent MeterProvider, so these views never affect user-configured backends.
7781
static void registerBuiltinMetrics(
7882
MetricExporter metricExporter, SdkMeterProviderBuilder builder) {
7983
registerGaxViews(builder);
@@ -94,12 +98,11 @@ private static void registerGaxViews(SdkMeterProviderBuilder builder) {
9498
for (String metricName : TelemetryConstants.GAX_METRICS) {
9599
Aggregation aggregation = Aggregation.defaultAggregation();
96100

97-
// Differentiate the instrumentation type between `count` vs `latency`
98101
InstrumentType type = InstrumentType.COUNTER;
99102
String unit = "1";
100103

101104
// Latency metrics use histograms with specific millisecond buckets.
102-
if (metricName.endsWith("latency")) {
105+
if (TelemetryConstants.GAX_HISTOGRAM_METRICS.contains(metricName)) {
103106
aggregation = AGGREGATION_WITH_MILLIS_HISTOGRAM;
104107
type = InstrumentType.HISTOGRAM;
105108
unit = "ms";

java-datastore/google-cloud-datastore/src/main/java/com/google/cloud/datastore/telemetry/DatastoreMetricsRecorder.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,6 @@
4343
@InternalExtensionOnly
4444
public interface DatastoreMetricsRecorder extends MetricsRecorder {
4545

46-
Logger logger = Logger.getLogger(DatastoreMetricsRecorder.class.getName());
47-
4846
/**
4947
* Releases any resources held by this recorder.
5048
*
@@ -85,6 +83,7 @@ default void close() {}
8583
* @return a {@link DatastoreMetricsRecorder} that fans out to all configured backends
8684
*/
8785
static DatastoreMetricsRecorder getInstance(@Nonnull DatastoreOptions datastoreOptions) {
86+
Logger logger = Logger.getLogger(DatastoreMetricsRecorder.class.getName());
8887
DatastoreOpenTelemetryOptions otelOptions = datastoreOptions.getOpenTelemetryOptions();
8988
List<DatastoreMetricsRecorder> recorders = new ArrayList<>();
9089

@@ -103,7 +102,7 @@ static DatastoreMetricsRecorder getInstance(@Nonnull DatastoreOptions datastoreO
103102
if (builtInOtel != null) {
104103
recorders.add(
105104
new OpenTelemetryDatastoreMetricsRecorder(
106-
builtInOtel, TelemetryConstants.METRIC_PREFIX, /* ownsOpenTelemetry= */ true));
105+
builtInOtel, TelemetryConstants.METRIC_PREFIX, /* isBuiltIn= */ true));
107106
}
108107
} catch (Exception e) {
109108
logger.log(

java-datastore/google-cloud-datastore/src/main/java/com/google/cloud/datastore/telemetry/OpenTelemetryDatastoreMetricsRecorder.java

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -52,28 +52,28 @@ class OpenTelemetryDatastoreMetricsRecorder extends OpenTelemetryMetricsRecorder
5252
private final OpenTelemetry openTelemetry;
5353
// True when this recorder created the OpenTelemetry instance (built-in path) and therefore
5454
// owns its lifecycle. False when the instance was provided by the user.
55-
private final boolean ownsOpenTelemetry;
55+
private final boolean isBuiltIn;
5656

5757
// Datastore-specific transaction metrics (registered under the Datastore meter).
5858
private final DoubleHistogram transactionLatency;
5959
private final LongCounter transactionAttemptCount;
6060

6161
/** Creates a recorder backed by a user-provided {@link OpenTelemetry} instance. */
6262
OpenTelemetryDatastoreMetricsRecorder(@Nonnull OpenTelemetry openTelemetry, String metricPrefix) {
63-
this(openTelemetry, metricPrefix, /* ownsOpenTelemetry= */ false);
63+
this(openTelemetry, metricPrefix, /* isBuiltIn= */ false);
6464
}
6565

6666
/**
6767
* Creates a recorder, specifying whether this instance owns the {@link OpenTelemetry} lifecycle.
6868
*
69-
* @param ownsOpenTelemetry {@code true} if this recorder created the instance and should shut it
70-
* down on {@link #close()}; {@code false} if the user provided it.
69+
* @param isBuiltIn {@code true} if this recorder created the instance and should shut it down on
70+
* {@link #close()}; {@code false} if the user provided it.
7171
*/
7272
OpenTelemetryDatastoreMetricsRecorder(
73-
@Nonnull OpenTelemetry openTelemetry, String metricPrefix, boolean ownsOpenTelemetry) {
73+
@Nonnull OpenTelemetry openTelemetry, String metricPrefix, boolean isBuiltIn) {
7474
super(openTelemetry, metricPrefix);
7575
this.openTelemetry = openTelemetry;
76-
this.ownsOpenTelemetry = ownsOpenTelemetry;
76+
this.isBuiltIn = isBuiltIn;
7777

7878
// Note: Standard GAX RPC metrics (operation_latency, attempt_latency, etc.) are handled by the
7979
// base OpenTelemetryMetricsRecorder class. Those metrics are inherited from the parent classes.
@@ -108,7 +108,7 @@ OpenTelemetry getOpenTelemetry() {
108108
*/
109109
@Override
110110
public void close() {
111-
if (ownsOpenTelemetry && openTelemetry instanceof OpenTelemetrySdk) {
111+
if (isBuiltIn && openTelemetry instanceof OpenTelemetrySdk) {
112112
try {
113113
((OpenTelemetrySdk) openTelemetry).close();
114114
} catch (Exception e) {

java-datastore/google-cloud-datastore/src/main/java/com/google/cloud/datastore/telemetry/TelemetryConstants.java

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,11 @@ public class TelemetryConstants {
6363
// GAX-emitted metrics for the built-in Cloud Monitoring export pipeline.
6464
public static final Set<String> GAX_METRICS = GAX_METRIC_NAME_MAP.keySet();
6565

66+
// The subset of GAX_METRICS that are histograms (latency metrics). Explicit enumeration here
67+
// avoids fragile name-suffix matching (endsWith("latency")) in view registration logic.
68+
public static final Set<String> GAX_HISTOGRAM_METRICS =
69+
ImmutableSet.of(METRIC_NAME_SHORT_OPERATION_LATENCY, METRIC_NAME_SHORT_ATTEMPT_LATENCY);
70+
6671
// Monitored resource type for Cloud Monitoring
6772
public static final String DATASTORE_RESOURCE_TYPE = "global";
6873

@@ -136,17 +141,21 @@ public class TelemetryConstants {
136141
/**
137142
* Metric name for the total latency of an operation (one full RPC call including retries).
138143
*
139-
* <p>Renamed to the plural form ({@code operation_latencies}) by {@link
140-
* DatastoreBuiltInMetricsView} during export.
144+
* <p><b>Singular/plural naming:</b> This constant uses the singular form ({@code
145+
* operation_latency}) to match the name recorded by the GAX layer and used by custom OTel
146+
* backends (e.g., an in-memory reader in tests). The built-in Cloud Monitoring export path
147+
* renames the metric to the plural form ({@code operation_latencies}) via an OTel View in {@link
148+
* DatastoreBuiltInMetricsView}. This split is intentional and consistent with other Google Cloud
149+
* client libraries.
141150
*/
142151
public static final String METRIC_NAME_OPERATION_LATENCY =
143152
METRIC_PREFIX + "/" + METRIC_NAME_SHORT_OPERATION_LATENCY;
144153

145154
/**
146155
* Metric name for the latency of a single RPC attempt.
147156
*
148-
* <p>Renamed to the plural form ({@code attempt_latencies}) by {@link
149-
* DatastoreBuiltInMetricsView} during export.
157+
* <p>See {@link #METRIC_NAME_OPERATION_LATENCY} for the singular/plural naming rationale. The
158+
* built-in Cloud Monitoring export renames this to {@code attempt_latencies} via an OTel View.
150159
*/
151160
public static final String METRIC_NAME_ATTEMPT_LATENCY =
152161
METRIC_PREFIX + "/" + METRIC_NAME_SHORT_ATTEMPT_LATENCY;

java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/DatastoreBuiltInAndCustomMetricsIT.java

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -175,17 +175,18 @@ public void bothBackendsActive_recorderIsComposite() {
175175
}
176176

177177
/**
178-
* Verifies that the built-in metrics export flag is on by default so that the Cloud Monitoring
179-
* exporter path is active even when no additional configuration is provided.
178+
* Verifies that the built-in metrics export flag is off by default. The flag is disabled until
179+
* the Datastore namespace in Cloud Monitoring is deployed; it must be explicitly opted in via
180+
* {@link DatastoreOpenTelemetryOptions.Builder#setExportBuiltinMetricsToGoogleCloudMonitoring}.
180181
*/
181182
@Test
182-
public void builtInMetricsExport_isEnabledByDefault() {
183+
public void builtInMetricsExport_isDisabledByDefault() {
183184
assertThat(
184185
datastore
185186
.getOptions()
186187
.getOpenTelemetryOptions()
187188
.isExportBuiltinMetricsToGoogleCloudMonitoring())
188-
.isTrue();
189+
.isFalse();
189190
}
190191

191192
/**

java-datastore/google-cloud-datastore/src/test/java/com/google/cloud/datastore/telemetry/DatastoreCloudMonitoringExporterTest.java

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -173,15 +173,17 @@ public void testClientCacheReferenceCounting() {
173173
DatastoreCloudMonitoringExporter.METRICS_CLIENT_CACHE.put(key, cachedMetricsClient);
174174

175175
DatastoreCloudMonitoringExporter exporter1 =
176-
new DatastoreCloudMonitoringExporter(key, PROJECT_ID, DATABASE_ID, mockClient, Collections.emptyMap());
176+
new DatastoreCloudMonitoringExporter(
177+
key, PROJECT_ID, DATABASE_ID, mockClient, Collections.emptyMap());
177178

178179
// First shutdown should decrement refCount to 1, but not close client
179180
exporter1.shutdown();
180181
assertThat(cachedMetricsClient.refCount.get()).isEqualTo(1);
181182
assertThat(DatastoreCloudMonitoringExporter.METRICS_CLIENT_CACHE.containsKey(key)).isTrue();
182183

183184
DatastoreCloudMonitoringExporter exporter2 =
184-
new DatastoreCloudMonitoringExporter(key, PROJECT_ID, DATABASE_ID, mockClient, Collections.emptyMap());
185+
new DatastoreCloudMonitoringExporter(
186+
key, PROJECT_ID, DATABASE_ID, mockClient, Collections.emptyMap());
185187

186188
// Second shutdown should decrement refCount to 0, close client, and remove from cache
187189
exporter2.shutdown();

0 commit comments

Comments
 (0)