Skip to content

Commit cfb2af3

Browse files
authored
chore: Add MetricsRecorder interface to Datastore (#12028)
Adds the Otel Metrics Recording Implementation as well as the No-Op Metrics Recording implementation for Datastore. The implementation will be chosen based on the opt-in or opt-out choice for Metrics recording.
1 parent d8fd05f commit cfb2af3

File tree

8 files changed

+287
-55
lines changed

8 files changed

+287
-55
lines changed

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

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@
1616

1717
package com.google.cloud.datastore;
1818

19-
import static com.google.cloud.datastore.telemetry.TraceUtil.ATTRIBUTES_KEY_DEFERRED;
20-
import static com.google.cloud.datastore.telemetry.TraceUtil.ATTRIBUTES_KEY_DOCUMENT_COUNT;
21-
import static com.google.cloud.datastore.telemetry.TraceUtil.ATTRIBUTES_KEY_MISSING;
22-
import static com.google.cloud.datastore.telemetry.TraceUtil.ATTRIBUTES_KEY_MORE_RESULTS;
23-
import static com.google.cloud.datastore.telemetry.TraceUtil.ATTRIBUTES_KEY_READ_CONSISTENCY;
24-
import static com.google.cloud.datastore.telemetry.TraceUtil.ATTRIBUTES_KEY_RECEIVED;
25-
import static com.google.cloud.datastore.telemetry.TraceUtil.ATTRIBUTES_KEY_TRANSACTIONAL;
26-
import static com.google.cloud.datastore.telemetry.TraceUtil.ATTRIBUTES_KEY_TRANSACTION_ID;
19+
import static com.google.cloud.datastore.telemetry.TelemetryConstants.ATTRIBUTES_KEY_DEFERRED;
20+
import static com.google.cloud.datastore.telemetry.TelemetryConstants.ATTRIBUTES_KEY_DOCUMENT_COUNT;
21+
import static com.google.cloud.datastore.telemetry.TelemetryConstants.ATTRIBUTES_KEY_MISSING;
22+
import static com.google.cloud.datastore.telemetry.TelemetryConstants.ATTRIBUTES_KEY_MORE_RESULTS;
23+
import static com.google.cloud.datastore.telemetry.TelemetryConstants.ATTRIBUTES_KEY_READ_CONSISTENCY;
24+
import static com.google.cloud.datastore.telemetry.TelemetryConstants.ATTRIBUTES_KEY_RECEIVED;
25+
import static com.google.cloud.datastore.telemetry.TelemetryConstants.ATTRIBUTES_KEY_TRANSACTIONAL;
26+
import static com.google.cloud.datastore.telemetry.TelemetryConstants.ATTRIBUTES_KEY_TRANSACTION_ID;
2727
import static com.google.cloud.datastore.telemetry.TraceUtil.SPAN_NAME_ALLOCATE_IDS;
2828
import static com.google.cloud.datastore.telemetry.TraceUtil.SPAN_NAME_BEGIN_TRANSACTION;
2929
import static com.google.cloud.datastore.telemetry.TraceUtil.SPAN_NAME_COMMIT;

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ public DatastoreOpenTelemetryOptions.Builder setTracingEnabled(boolean enabled)
110110
* @return the builder instance.
111111
*/
112112
@Nonnull
113-
public DatastoreOpenTelemetryOptions.Builder setMetricsEnabled(boolean enabled) {
113+
DatastoreOpenTelemetryOptions.Builder setMetricsEnabled(boolean enabled) {
114114
this.metricsEnabled = enabled;
115115
return this;
116116
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
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.telemetry;
18+
19+
import com.google.cloud.datastore.DatastoreOpenTelemetryOptions;
20+
import io.opentelemetry.api.GlobalOpenTelemetry;
21+
import io.opentelemetry.api.OpenTelemetry;
22+
import java.util.Map;
23+
import javax.annotation.Nonnull;
24+
25+
/** Interface to record specific metric operations. */
26+
public interface MetricsRecorder {
27+
/** Records the total latency of a transaction in milliseconds. */
28+
void recordTransactionLatency(double latencyMs, Map<String, String> attributes);
29+
30+
/** Records the number of attempts a transaction took. */
31+
void recordTransactionAttemptCount(long count, Map<String, String> attributes);
32+
33+
/**
34+
* Returns a {@link MetricsRecorder} instance based on the provided OpenTelemetry options.
35+
*
36+
* @param options The {@link com.google.cloud.datastore.DatastoreOpenTelemetryOptions} configuring
37+
* telemetry.
38+
* @return An {@link OpenTelemetryMetricsRecorder} if metrics are enabled, otherwise a {@link
39+
* NoOpMetricsRecorder}.
40+
*/
41+
static MetricsRecorder getInstance(@Nonnull DatastoreOpenTelemetryOptions options) {
42+
boolean isMetricsEnabled = options.isMetricsEnabled();
43+
44+
if (isMetricsEnabled) {
45+
OpenTelemetry openTelemetry = options.getOpenTelemetry();
46+
if (openTelemetry == null) {
47+
return new OpenTelemetryMetricsRecorder(GlobalOpenTelemetry.get());
48+
}
49+
return new OpenTelemetryMetricsRecorder(openTelemetry);
50+
} else {
51+
return new NoOpMetricsRecorder();
52+
}
53+
}
54+
}
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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.telemetry;
18+
19+
import java.util.Map;
20+
21+
/**
22+
* Metrics recorder implementation, used to stub out metrics instrumentation when metrics are
23+
* disabled.
24+
*/
25+
class NoOpMetricsRecorder implements MetricsRecorder {
26+
27+
@Override
28+
public void recordTransactionLatency(double latencyMs, Map<String, String> attributes) {
29+
/* No-Op OTel Operation */
30+
}
31+
32+
@Override
33+
public void recordTransactionAttemptCount(long count, Map<String, String> attributes) {
34+
/* No-Op OTel Operation */
35+
}
36+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
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.telemetry;
18+
19+
import io.opentelemetry.api.OpenTelemetry;
20+
import io.opentelemetry.api.common.Attributes;
21+
import io.opentelemetry.api.common.AttributesBuilder;
22+
import io.opentelemetry.api.metrics.DoubleHistogram;
23+
import io.opentelemetry.api.metrics.LongCounter;
24+
import io.opentelemetry.api.metrics.Meter;
25+
import java.util.Map;
26+
import javax.annotation.Nonnull;
27+
28+
/**
29+
* OpenTelemetry metrics recorder implementation, used to record metrics when metrics are enabled.
30+
*/
31+
class OpenTelemetryMetricsRecorder implements MetricsRecorder {
32+
private final OpenTelemetry openTelemetry;
33+
34+
private final DoubleHistogram transactionLatency;
35+
private final LongCounter transactionAttemptCount;
36+
37+
OpenTelemetryMetricsRecorder(@Nonnull OpenTelemetry openTelemetry) {
38+
this.openTelemetry = openTelemetry;
39+
40+
Meter meter = openTelemetry.getMeter(TelemetryConstants.METER_NAME);
41+
42+
this.transactionLatency =
43+
meter
44+
.histogramBuilder(TelemetryConstants.SERVICE_NAME + "/transaction_latency")
45+
.setDescription("Total latency for successful transaction operations")
46+
.setUnit("ms")
47+
.build();
48+
49+
this.transactionAttemptCount =
50+
meter
51+
.counterBuilder(TelemetryConstants.SERVICE_NAME + "/transaction_attempt_count")
52+
.setDescription("Number of attempts to commit a transaction")
53+
.build();
54+
}
55+
56+
OpenTelemetry getOpenTelemetry() {
57+
return openTelemetry;
58+
}
59+
60+
@Override
61+
public void recordTransactionLatency(double latencyMs, Map<String, String> attributes) {
62+
transactionLatency.record(latencyMs, toOtelAttributes(attributes));
63+
}
64+
65+
@Override
66+
public void recordTransactionAttemptCount(long count, Map<String, String> attributes) {
67+
transactionAttemptCount.add(count, toOtelAttributes(attributes));
68+
}
69+
70+
private static Attributes toOtelAttributes(Map<String, String> attributes) {
71+
AttributesBuilder builder = Attributes.builder();
72+
if (attributes != null) {
73+
attributes.forEach(builder::put);
74+
}
75+
return builder.build();
76+
}
77+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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.telemetry;
18+
19+
import com.google.api.core.InternalApi;
20+
21+
/** Internal telemetry constants shared between OpenTelemetry tracing and metrics. */
22+
@InternalApi
23+
public class TelemetryConstants {
24+
static final String SERVICE_NAME = "datastore.googleapis.com";
25+
static final String METER_NAME = "com.google.cloud.datastore";
26+
27+
public static final String ATTRIBUTES_KEY_DOCUMENT_COUNT = "doc_count";
28+
public static final String ATTRIBUTES_KEY_TRANSACTIONAL = "transactional";
29+
public static final String ATTRIBUTES_KEY_TRANSACTION_ID = "transaction_id";
30+
public static final String ATTRIBUTES_KEY_READ_CONSISTENCY = "read_consistency";
31+
public static final String ATTRIBUTES_KEY_RECEIVED = "Received";
32+
public static final String ATTRIBUTES_KEY_MISSING = "Missing";
33+
public static final String ATTRIBUTES_KEY_DEFERRED = "Deferred";
34+
public static final String ATTRIBUTES_KEY_MORE_RESULTS = "more_results";
35+
36+
/* TODO(lawrenceqiu): For now, these are a duplicate of method names in TraceUtil. Those will use these eventually */
37+
// Format is not SnakeCase to keep backward compatibility with the existing values TraceUtil spans
38+
public static final String METHOD_ALLOCATE_IDS = "AllocateIds";
39+
public static final String METHOD_BEGIN_TRANSACTION = "Transaction.Begin";
40+
public static final String METHOD_COMMIT = "Commit";
41+
public static final String METHOD_LOOKUP = "Lookup";
42+
public static final String METHOD_RESERVE_IDS = "ReserveIds";
43+
public static final String METHOD_RUN_QUERY = "RunQuery";
44+
public static final String METHOD_TRANSACTION_COMMIT = "Transaction.Commit";
45+
public static final String METHOD_TRANSACTION_LOOKUP = "Transaction.Lookup";
46+
public static final String METHOD_TRANSACTION_RUN = "Transaction.Run";
47+
public static final String METHOD_TRANSACTION_RUN_QUERY = "Transaction.RunQuery";
48+
public static final String METHOD_TRANSACTION_ROLLBACK = "Transaction.Rollback";
49+
public static final String METHOD_TRANSACTION_RUN_AGGREGATION_QUERY =
50+
"Transaction.RunAggregationQuery";
51+
public static final String METHOD_ADD = "add";
52+
public static final String METHOD_PUT = "put";
53+
public static final String METHOD_UPDATE = "update";
54+
public static final String METHOD_DELETE = "delete";
55+
public static final String METHOD_SUBMIT = "submit";
56+
57+
private TelemetryConstants() {}
58+
}

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

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,14 +46,6 @@ public interface TraceUtil {
4646
String SPAN_NAME_TRANSACTION_RUN_QUERY = "Transaction.RunQuery";
4747
String SPAN_NAME_ROLLBACK = "Transaction.Rollback";
4848
String SPAN_NAME_TRANSACTION_RUN_AGGREGATION_QUERY = "Transaction.RunAggregationQuery";
49-
String ATTRIBUTES_KEY_DOCUMENT_COUNT = "doc_count";
50-
String ATTRIBUTES_KEY_TRANSACTIONAL = "transactional";
51-
String ATTRIBUTES_KEY_TRANSACTION_ID = "transaction_id";
52-
String ATTRIBUTES_KEY_READ_CONSISTENCY = "read_consistency";
53-
String ATTRIBUTES_KEY_RECEIVED = "Received";
54-
String ATTRIBUTES_KEY_MISSING = "Missing";
55-
String ATTRIBUTES_KEY_DEFERRED = "Deferred";
56-
String ATTRIBUTES_KEY_MORE_RESULTS = "more_results";
5749

5850
/**
5951
* Creates and returns an instance of the TraceUtil class.

0 commit comments

Comments
 (0)