Skip to content

Commit 842d262

Browse files
committed
feat(datastore): Add built-in Cloud Monitoring metrics export
Add a default-on, private OpenTelemetry SDK pipeline that automatically exports Datastore client-side metrics to Google Cloud Monitoring without requiring any user configuration. Key additions: - DatastoreBuiltInMetricsView: registers OTel views for metric renaming (GAX prefix → custom.googleapis.com/internal/client), custom histogram bucket boundaries, and an attribute allowlist that prevents unexpected labels from reaching Cloud Monitoring - DatastoreCloudMonitoringExporter: MetricExporter impl that batches TimeSeries and calls MetricServiceClient.createTimeSeries(); graceful degradation (logs warnings, never crashes); best-effort flush() - DatastoreCloudMonitoringExporterUtils: converts OTel MetricData to Cloud Monitoring TimeSeries protos - BuiltInDatastoreMetricsProvider: creates a private OpenTelemetrySdk per Datastore client; computes stable client_uid and client_name attributes; registers a shutdown hook as a last-resort safety net - CompositeDatastoreMetricsRecorder: fans out all record*() calls to both the built-in and any user-provided backends simultaneously - DatastoreMetricsRecorder.getInstance(): updated to composite factory that wires up the built-in path (unless emulator or env-var disabled) alongside any custom OTel backend; adds default close() for lifecycle - OpenTelemetryDatastoreMetricsRecorder: add ownsOpenTelemetry flag and close() so the built-in SDK is flushed when DatastoreImpl.close() runs - DatastoreOpenTelemetryOptions: add exportBuiltinMetricsToGoogleCloudMonitoring flag (default true) with @BetaApi getter and setter; clarify isEnabled() Javadoc to note it does not cover the built-in path - DatastoreImpl.close(): call getMetricsRecorder().close() after RPC channel teardown so the built-in SDK flushes buffered metrics - pom.xml: promote opentelemetry-sdk, opentelemetry-sdk-common, and opentelemetry-sdk-metrics from test→compile scope; add google-cloud-monitoring and proto-google-cloud-monitoring-v3 compile deps - New tests: DatastoreCloudMonitoringExporterTest, BuiltInDatastoreMetricsProviderTest, DatastoreMetricsRecorderTest (updated), DatastoreBuiltInAndCustomMetricsIT
1 parent 0bf197b commit 842d262

14 files changed

Lines changed: 1958 additions & 38 deletions

java-datastore/google-cloud-datastore/pom.xml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -196,12 +196,20 @@
196196
<dependency>
197197
<groupId>io.opentelemetry</groupId>
198198
<artifactId>opentelemetry-sdk</artifactId>
199-
<scope>test</scope>
200199
</dependency>
201200
<dependency>
202201
<groupId>io.opentelemetry</groupId>
203202
<artifactId>opentelemetry-sdk-common</artifactId>
204-
<scope>test</scope>
203+
</dependency>
204+
<dependency>
205+
<groupId>com.google.cloud</groupId>
206+
<artifactId>google-cloud-monitoring</artifactId>
207+
<version>3.90.0-SNAPSHOT</version><!-- {x-version-update:google-cloud-monitoring:current} -->
208+
</dependency>
209+
<dependency>
210+
<groupId>com.google.api.grpc</groupId>
211+
<artifactId>proto-google-cloud-monitoring-v3</artifactId>
212+
<version>3.90.0-SNAPSHOT</version><!-- {x-version-update:google-cloud-monitoring:current} -->
205213
</dependency>
206214
<dependency>
207215
<groupId>io.opentelemetry</groupId>
@@ -216,7 +224,6 @@
216224
<dependency>
217225
<groupId>io.opentelemetry</groupId>
218226
<artifactId>opentelemetry-sdk-metrics</artifactId>
219-
<scope>test</scope>
220227
</dependency>
221228
<dependency>
222229
<groupId>io.opentelemetry</groupId>

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,13 +162,24 @@ public T call() throws DatastoreException {
162162
}
163163
}
164164

165+
/**
166+
* Closes the Datastore client and releases all resources.
167+
*
168+
* <p>This method closes the underlying RPC channel and then closes the {@link
169+
* com.google.cloud.datastore.telemetry.DatastoreMetricsRecorder}. For clients using the
170+
* built-in Cloud Monitoring exporter, closing the recorder flushes any buffered metrics and
171+
* shuts down the private {@link io.opentelemetry.sdk.OpenTelemetrySdk} instance. For clients
172+
* using a user-provided {@link io.opentelemetry.api.OpenTelemetry} instance, the recorder close
173+
* is a no-op since the user owns that instance's lifecycle.
174+
*/
165175
@Override
166176
public void close() throws Exception {
167177
try {
168178
datastoreRpc.close();
169179
} catch (Exception e) {
170180
logger.log(Level.WARNING, "Failed to close channels", e);
171181
}
182+
getOptions().getMetricsRecorder().close();
172183
}
173184

174185
@Override

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

Lines changed: 81 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,25 +16,39 @@
1616

1717
package com.google.cloud.datastore;
1818

19+
import com.google.api.core.BetaApi;
1920
import io.opentelemetry.api.OpenTelemetry;
2021
import javax.annotation.Nonnull;
2122
import javax.annotation.Nullable;
2223

24+
/**
25+
* Represents the options that are used to configure the use of OpenTelemetry for telemetry
26+
* collection in the Datastore SDK.
27+
*/
2328
public class DatastoreOpenTelemetryOptions {
2429
private final boolean tracingEnabled;
2530
private final boolean metricsEnabled;
31+
private final boolean exportBuiltinMetricsToGoogleCloudMonitoring;
2632
private final @Nullable OpenTelemetry openTelemetry;
2733

2834
DatastoreOpenTelemetryOptions(Builder builder) {
2935
this.tracingEnabled = builder.tracingEnabled;
3036
this.metricsEnabled = builder.metricsEnabled;
37+
this.exportBuiltinMetricsToGoogleCloudMonitoring =
38+
builder.exportBuiltinMetricsToGoogleCloudMonitoring;
3139
this.openTelemetry = builder.openTelemetry;
3240
}
3341

3442
/**
35-
* Returns whether either tracing or metrics are enabled. Telemetry is disabled by default.
43+
* Returns whether either tracing or custom metrics (via a user-provided {@link OpenTelemetry}
44+
* instance) are enabled.
45+
*
46+
* <p><b>Note:</b> This method does <em>not</em> reflect the state of built-in metrics export to
47+
* Google Cloud Monitoring, which is controlled separately by {@link
48+
* #isExportBuiltinMetricsToGoogleCloudMonitoring()} and is {@code true} by default. To check
49+
* whether any telemetry is active, also consult that flag.
3650
*
37-
* @return {@code true} if either tracing or metrics are enabled, {@code false} otherwise.
51+
* @return {@code true} if tracing or custom OTel metrics are enabled, {@code false} otherwise.
3852
*/
3953
public boolean isEnabled() {
4054
return tracingEnabled || metricsEnabled;
@@ -50,24 +64,54 @@ public boolean isTracingEnabled() {
5064
}
5165

5266
/**
53-
* Returns whether metrics are enabled.
67+
* Returns whether metrics are enabled for the custom (user-provided) OpenTelemetry backend.
5468
*
5569
* @return {@code true} if metrics are enabled, {@code false} otherwise.
5670
*/
5771
public boolean isMetricsEnabled() {
5872
return metricsEnabled;
5973
}
6074

75+
/**
76+
* Returns whether built-in metrics should be exported to Google Cloud Monitoring.
77+
*
78+
* <p>When enabled, client-side metrics are automatically exported to Google Cloud Monitoring
79+
* using the Cloud Monitoring API. This is independent of the custom OpenTelemetry backend
80+
* configured via {@link #getOpenTelemetry()}.
81+
*
82+
* @return {@code true} if built-in metrics export to Cloud Monitoring is enabled, {@code false}
83+
* otherwise.
84+
*/
85+
@BetaApi
86+
public boolean isExportBuiltinMetricsToGoogleCloudMonitoring() {
87+
return exportBuiltinMetricsToGoogleCloudMonitoring;
88+
}
89+
90+
/**
91+
* Returns the custom {@link OpenTelemetry} instance, if one was provided.
92+
*
93+
* @return the custom {@link OpenTelemetry} instance, or {@code null} if none was provided.
94+
*/
6195
@Nullable
6296
public OpenTelemetry getOpenTelemetry() {
6397
return openTelemetry;
6498
}
6599

100+
/**
101+
* Returns a new {@link Builder} initialized with the values from this options instance.
102+
*
103+
* @return a new {@link Builder}.
104+
*/
66105
@Nonnull
67106
public DatastoreOpenTelemetryOptions.Builder toBuilder() {
68107
return new DatastoreOpenTelemetryOptions.Builder(this);
69108
}
70109

110+
/**
111+
* Returns a new default {@link Builder}.
112+
*
113+
* @return a new {@link Builder}.
114+
*/
71115
@Nonnull
72116
public static DatastoreOpenTelemetryOptions.Builder newBuilder() {
73117
return new DatastoreOpenTelemetryOptions.Builder();
@@ -77,25 +121,30 @@ public static class Builder {
77121

78122
private boolean tracingEnabled;
79123
private boolean metricsEnabled;
124+
private boolean exportBuiltinMetricsToGoogleCloudMonitoring;
80125

81126
@Nullable private OpenTelemetry openTelemetry;
82127

83128
private Builder() {
84129
tracingEnabled = false;
85130
metricsEnabled = false;
131+
exportBuiltinMetricsToGoogleCloudMonitoring = true;
86132
openTelemetry = null;
87133
}
88134

89135
private Builder(DatastoreOpenTelemetryOptions options) {
90136
this.tracingEnabled = options.tracingEnabled;
91137
this.metricsEnabled = options.metricsEnabled;
138+
this.exportBuiltinMetricsToGoogleCloudMonitoring =
139+
options.exportBuiltinMetricsToGoogleCloudMonitoring;
92140
this.openTelemetry = options.openTelemetry;
93141
}
94142

95143
/**
96144
* Sets whether tracing should be enabled.
97145
*
98146
* @param enabled Whether tracing should be enabled.
147+
* @return this builder instance.
99148
*/
100149
@Nonnull
101150
public DatastoreOpenTelemetryOptions.Builder setTracingEnabled(boolean enabled) {
@@ -104,23 +153,43 @@ public DatastoreOpenTelemetryOptions.Builder setTracingEnabled(boolean enabled)
104153
}
105154

106155
/**
107-
* Sets whether metrics should be enabled.
156+
* Sets whether metrics should be enabled for the custom (user-provided) OpenTelemetry backend.
108157
*
109158
* @param enabled Whether metrics should be enabled.
110-
* @return the builder instance.
159+
* @return this builder instance.
111160
*/
112161
@Nonnull
113162
DatastoreOpenTelemetryOptions.Builder setMetricsEnabled(boolean enabled) {
114163
this.metricsEnabled = enabled;
115164
return this;
116165
}
117166

167+
/**
168+
* Sets whether built-in metrics should be exported to Google Cloud Monitoring.
169+
*
170+
* <p>When enabled (the default), client-side metrics are automatically exported to Google Cloud
171+
* Monitoring using the Cloud Monitoring API. This can be disabled to prevent metrics from being
172+
* sent to Cloud Monitoring while still allowing metrics to flow to a custom OpenTelemetry
173+
* backend.
174+
*
175+
* @param exportBuiltinMetrics Whether built-in metrics should be exported to Cloud Monitoring.
176+
* @return this builder instance.
177+
*/
178+
@BetaApi
179+
@Nonnull
180+
public DatastoreOpenTelemetryOptions.Builder setExportBuiltinMetricsToGoogleCloudMonitoring(
181+
boolean exportBuiltinMetrics) {
182+
this.exportBuiltinMetricsToGoogleCloudMonitoring = exportBuiltinMetrics;
183+
return this;
184+
}
185+
118186
/**
119187
* Sets the {@link OpenTelemetry} to use with this Datastore instance. If telemetry collection
120-
* is enabled, but an `OpenTelemetry` is not provided, the Datastore SDK will attempt to use the
121-
* `GlobalOpenTelemetry`.
188+
* is enabled, but an {@code OpenTelemetry} is not provided, the Datastore SDK will attempt to
189+
* use the {@code GlobalOpenTelemetry}.
122190
*
123191
* @param openTelemetry The OpenTelemetry that should be used by this Datastore instance.
192+
* @return this builder instance.
124193
*/
125194
@Nonnull
126195
public DatastoreOpenTelemetryOptions.Builder setOpenTelemetry(
@@ -129,6 +198,11 @@ public DatastoreOpenTelemetryOptions.Builder setOpenTelemetry(
129198
return this;
130199
}
131200

201+
/**
202+
* Builds a new {@link DatastoreOpenTelemetryOptions} instance from this builder.
203+
*
204+
* @return a new {@link DatastoreOpenTelemetryOptions}.
205+
*/
132206
@Nonnull
133207
public DatastoreOpenTelemetryOptions build() {
134208
return new DatastoreOpenTelemetryOptions(this);

0 commit comments

Comments
 (0)