Skip to content

Commit e6bf149

Browse files
authored
chore: Collect Operation and Attempt metrics for gRPC requests (#12060)
1 parent 28ba86e commit e6bf149

File tree

3 files changed

+86
-25
lines changed

3 files changed

+86
-25
lines changed

java-datastore/google-cloud-datastore/src/main/java/com/google/cloud/datastore/spi/v1/GrpcDatastoreRpc.java

Lines changed: 43 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,12 @@
3131
import com.google.api.gax.rpc.HeaderProvider;
3232
import com.google.api.gax.rpc.NoHeaderProvider;
3333
import com.google.api.gax.rpc.TransportChannel;
34+
import com.google.api.gax.tracing.MetricsTracerFactory;
35+
import com.google.api.gax.tracing.OpenTelemetryMetricsRecorder;
3436
import com.google.cloud.ServiceOptions;
3537
import com.google.cloud.datastore.DatastoreException;
3638
import com.google.cloud.datastore.DatastoreOptions;
39+
import com.google.cloud.datastore.telemetry.TelemetryConstants;
3740
import com.google.cloud.datastore.v1.DatastoreSettings;
3841
import com.google.cloud.datastore.v1.stub.DatastoreStubSettings;
3942
import com.google.cloud.datastore.v1.stub.GrpcDatastoreStub;
@@ -58,8 +61,12 @@
5861
import io.grpc.CallOptions;
5962
import io.grpc.ManagedChannel;
6063
import io.grpc.ManagedChannelBuilder;
64+
import io.opentelemetry.api.GlobalOpenTelemetry;
65+
import io.opentelemetry.api.OpenTelemetry;
6166
import java.io.IOException;
6267
import java.util.Collections;
68+
import java.util.HashMap;
69+
import java.util.Map;
6370

6471
@InternalApi
6572
public class GrpcDatastoreRpc implements DatastoreRpc {
@@ -76,7 +83,7 @@ public GrpcDatastoreRpc(DatastoreOptions datastoreOptions) throws IOException {
7683
: getClientContext(datastoreOptions);
7784

7885
/* For grpc transport options, configure default gRPC Connection pool with minChannelCount = 1 */
79-
DatastoreStubSettings datastoreStubSettings =
86+
DatastoreStubSettings.Builder builder =
8087
DatastoreStubSettings.newBuilder(clientContext)
8188
.applyToAllUnaryMethods(retrySettingSetter(datastoreOptions))
8289
.setTransportChannelProvider(
@@ -86,14 +93,46 @@ public GrpcDatastoreRpc(DatastoreOptions datastoreOptions) throws IOException {
8693
.setInitialChannelCount(DatastoreOptions.INIT_CHANNEL_COUNT)
8794
.setMinChannelCount(DatastoreOptions.MIN_CHANNEL_COUNT)
8895
.build())
89-
.build())
90-
.build();
91-
datastoreStub = GrpcDatastoreStub.create(datastoreStubSettings);
96+
.build());
97+
98+
// Hook into Gax's Metrics collection framework
99+
MetricsTracerFactory metricsTracerFactory = buildMetricsTracerFactory(datastoreOptions);
100+
if (metricsTracerFactory != null) {
101+
builder.setTracerFactory(metricsTracerFactory);
102+
}
103+
104+
datastoreStub = GrpcDatastoreStub.create(builder.build());
92105
} catch (IOException e) {
93106
throw new IOException(e);
94107
}
95108
}
96109

110+
/**
111+
* Build the MetricsTracerFactory to hook into Gax's Otel Framework. Only hooks into Gax on two
112+
* conditions: 1. OpenTelemetry instance is passed in by the user 2. Metrics are enabled
113+
*
114+
* <p>Sets default attributes to be recorded as part of the metrics.
115+
*/
116+
static MetricsTracerFactory buildMetricsTracerFactory(DatastoreOptions datastoreOptions) {
117+
if (!datastoreOptions.getOpenTelemetryOptions().isMetricsEnabled()) {
118+
return null;
119+
}
120+
OpenTelemetry openTelemetry = datastoreOptions.getOpenTelemetryOptions().getOpenTelemetry();
121+
if (openTelemetry == null) {
122+
openTelemetry = GlobalOpenTelemetry.get();
123+
}
124+
OpenTelemetryMetricsRecorder gaxMetricsRecorder =
125+
new OpenTelemetryMetricsRecorder(openTelemetry, TelemetryConstants.SERVICE_NAME);
126+
Map<String, String> attributes = new HashMap<>();
127+
attributes.put(TelemetryConstants.ATTRIBUTES_KEY_PROJECT_ID, datastoreOptions.getProjectId());
128+
if (!Strings.isNullOrEmpty(datastoreOptions.getDatabaseId())) {
129+
attributes.put(
130+
TelemetryConstants.ATTRIBUTES_KEY_DATABASE_ID, datastoreOptions.getDatabaseId());
131+
}
132+
attributes.put(TelemetryConstants.ATTRIBUTES_KEY_TRANSPORT, "grpc");
133+
return new MetricsTracerFactory(gaxMetricsRecorder, attributes);
134+
}
135+
97136
@Override
98137
public void close() throws Exception {
99138
if (!closed) {

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

Lines changed: 9 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,10 @@
2929
*/
3030
@InternalApi
3131
public class TelemetryConstants {
32-
static final String SERVICE_NAME = "datastore.googleapis.com";
32+
33+
// TODO(lawrenceqiu): For now, use `custom.googleapis.com` until metrics can be written to
34+
// datastore domain
35+
public static final String SERVICE_NAME = "custom.googleapis.com";
3336
static final String METER_NAME = "com.google.cloud.datastore";
3437

3538
public static final String ATTRIBUTES_KEY_DOCUMENT_COUNT = "doc_count";
@@ -65,26 +68,11 @@ public class TelemetryConstants {
6568
public static final String METRIC_NAME_TRANSACTION_ATTEMPT_COUNT =
6669
SERVICE_NAME + "/client/transaction_attempt_count";
6770

68-
/* TODO(lawrenceqiu): For now, these are a duplicate of method names in TraceUtil. Those will use these eventually */
69-
// Format is not SnakeCase to keep backward compatibility with the existing values TraceUtil spans
70-
public static final String METHOD_ALLOCATE_IDS = "AllocateIds";
71-
public static final String METHOD_BEGIN_TRANSACTION = "Transaction.Begin";
72-
public static final String METHOD_COMMIT = "Commit";
73-
public static final String METHOD_LOOKUP = "Lookup";
74-
public static final String METHOD_RESERVE_IDS = "ReserveIds";
75-
public static final String METHOD_RUN_QUERY = "RunQuery";
76-
public static final String METHOD_TRANSACTION_COMMIT = "Transaction.Commit";
77-
public static final String METHOD_TRANSACTION_LOOKUP = "Transaction.Lookup";
78-
public static final String METHOD_TRANSACTION_RUN = "Transaction.Run";
79-
public static final String METHOD_TRANSACTION_RUN_QUERY = "Transaction.RunQuery";
80-
public static final String METHOD_TRANSACTION_ROLLBACK = "Transaction.Rollback";
81-
public static final String METHOD_TRANSACTION_RUN_AGGREGATION_QUERY =
82-
"Transaction.RunAggregationQuery";
83-
public static final String METHOD_ADD = "add";
84-
public static final String METHOD_PUT = "put";
85-
public static final String METHOD_UPDATE = "update";
86-
public static final String METHOD_DELETE = "delete";
87-
public static final String METHOD_SUBMIT = "submit";
71+
// Format is not SnakeCase to match the method name convention in Gax.
72+
// The format is {ServiceName}.{MethodName}. For these methods, include `Transaction`
73+
// to denote that the metrics are related specifically to transactions.
74+
public static final String METHOD_TRANSACTION_COMMIT = "Datastore.Transaction.Commit";
75+
public static final String METHOD_TRANSACTION_RUN = "Datastore.Transaction.Run";
8876

8977
private TelemetryConstants() {}
9078

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* Copyright 2026 Google LLC
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.google.cloud.datastore;
18+
19+
import io.opentelemetry.api.OpenTelemetry;
20+
21+
// TODO(lawrenceqiu): This is a temporary class used to enabled metrics while `setMetricsEnabled`
22+
// is package private. This is to be removed later.
23+
public class DatastoreOpenTelemetryOptionsTestHelper {
24+
public static DatastoreOpenTelemetryOptions withMetricsEnabled(OpenTelemetry openTelemetry) {
25+
return DatastoreOpenTelemetryOptions.newBuilder()
26+
.setMetricsEnabled(true)
27+
.setOpenTelemetry(openTelemetry)
28+
.build();
29+
}
30+
31+
public static DatastoreOpenTelemetryOptions withMetricsEnabled() {
32+
return DatastoreOpenTelemetryOptions.newBuilder().setMetricsEnabled(true).build();
33+
}
34+
}

0 commit comments

Comments
 (0)