-
Notifications
You must be signed in to change notification settings - Fork 1.1k
chore: Add MetricsRecorder interface to Datastore #12028
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
bf33238
ca9793d
e8eb8ac
22d9387
7a62c9e
f0dfc65
5a1819c
d061eb9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,54 @@ | ||
| /* | ||
| * Copyright 2026 Google LLC | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| package com.google.cloud.datastore.telemetry; | ||
|
|
||
| import com.google.cloud.datastore.DatastoreOpenTelemetryOptions; | ||
| import io.opentelemetry.api.GlobalOpenTelemetry; | ||
| import io.opentelemetry.api.OpenTelemetry; | ||
| import java.util.Map; | ||
| import javax.annotation.Nonnull; | ||
|
|
||
| /** Interface to record specific metric operations. */ | ||
| public interface MetricsRecorder { | ||
| /** Records the total latency of a transaction in milliseconds. */ | ||
| void recordTransactionLatency(double latencyMs, Map<String, String> attributes); | ||
|
|
||
| /** Records the number of attempts a transaction took. */ | ||
| void recordTransactionAttemptCount(long count, Map<String, String> attributes); | ||
|
|
||
| /** | ||
| * Returns a {@link MetricsRecorder} instance based on the provided OpenTelemetry options. | ||
| * | ||
| * @param options The {@link com.google.cloud.datastore.DatastoreOpenTelemetryOptions} configuring | ||
| * telemetry. | ||
| * @return An {@link OpenTelemetryMetricsRecorder} if metrics are enabled, otherwise a {@link | ||
| * NoOpMetricsRecorder}. | ||
| */ | ||
| static MetricsRecorder getInstance(@Nonnull DatastoreOpenTelemetryOptions options) { | ||
| boolean isMetricsEnabled = options.isMetricsEnabled(); | ||
|
|
||
| if (isMetricsEnabled) { | ||
| OpenTelemetry openTelemetry = options.getOpenTelemetry(); | ||
| if (openTelemetry == null) { | ||
| return new OpenTelemetryMetricsRecorder(GlobalOpenTelemetry.get()); | ||
| } | ||
| return new OpenTelemetryMetricsRecorder(openTelemetry); | ||
| } else { | ||
| return new NoOpMetricsRecorder(); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,36 @@ | ||
| /* | ||
| * Copyright 2026 Google LLC | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| package com.google.cloud.datastore.telemetry; | ||
|
|
||
| import java.util.Map; | ||
|
|
||
| /** | ||
| * Metrics recorder implementation, used to stub out metrics instrumentation when metrics are | ||
| * disabled. | ||
| */ | ||
| class NoOpMetricsRecorder implements MetricsRecorder { | ||
|
|
||
| @Override | ||
| public void recordTransactionLatency(double latencyMs, Map<String, String> attributes) { | ||
| /* No-Op OTel Operation */ | ||
| } | ||
|
|
||
| @Override | ||
| public void recordTransactionAttemptCount(long count, Map<String, String> attributes) { | ||
| /* No-Op OTel Operation */ | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,77 @@ | ||
| /* | ||
| * Copyright 2026 Google LLC | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| package com.google.cloud.datastore.telemetry; | ||
|
|
||
| import io.opentelemetry.api.OpenTelemetry; | ||
| import io.opentelemetry.api.common.Attributes; | ||
| import io.opentelemetry.api.common.AttributesBuilder; | ||
| import io.opentelemetry.api.metrics.DoubleHistogram; | ||
| import io.opentelemetry.api.metrics.LongCounter; | ||
| import io.opentelemetry.api.metrics.Meter; | ||
| import java.util.Map; | ||
| import javax.annotation.Nonnull; | ||
|
|
||
| /** | ||
| * OpenTelemetry metrics recorder implementation, used to record metrics when metrics are enabled. | ||
| */ | ||
| class OpenTelemetryMetricsRecorder implements MetricsRecorder { | ||
| private final OpenTelemetry openTelemetry; | ||
|
|
||
| private final DoubleHistogram transactionLatency; | ||
| private final LongCounter transactionAttemptCount; | ||
|
|
||
| OpenTelemetryMetricsRecorder(@Nonnull OpenTelemetry openTelemetry) { | ||
| this.openTelemetry = openTelemetry; | ||
|
|
||
| Meter meter = openTelemetry.getMeter(TelemetryConstants.METER_NAME); | ||
|
|
||
| this.transactionLatency = | ||
| meter | ||
| .histogramBuilder(TelemetryConstants.SERVICE_NAME + "/transaction_latency") | ||
| .setDescription("Total latency for successful transaction operations") | ||
| .setUnit("ms") | ||
| .build(); | ||
|
|
||
| this.transactionAttemptCount = | ||
| meter | ||
| .counterBuilder(TelemetryConstants.SERVICE_NAME + "/transaction_attempt_count") | ||
| .setDescription("Number of attempts to commit a transaction") | ||
| .build(); | ||
| } | ||
|
|
||
| OpenTelemetry getOpenTelemetry() { | ||
| return openTelemetry; | ||
| } | ||
|
|
||
| @Override | ||
| public void recordTransactionLatency(double latencyMs, Map<String, String> attributes) { | ||
| transactionLatency.record(latencyMs, toOtelAttributes(attributes)); | ||
| } | ||
|
|
||
| @Override | ||
| public void recordTransactionAttemptCount(long count, Map<String, String> attributes) { | ||
| transactionAttemptCount.add(count, toOtelAttributes(attributes)); | ||
| } | ||
|
|
||
| private static Attributes toOtelAttributes(Map<String, String> attributes) { | ||
| AttributesBuilder builder = Attributes.builder(); | ||
| if (attributes != null) { | ||
| attributes.forEach(builder::put); | ||
| } | ||
| return builder.build(); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,57 @@ | ||
| /* | ||
| * Copyright 2026 Google LLC | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| package com.google.cloud.datastore.telemetry; | ||
|
|
||
| import com.google.api.core.InternalApi; | ||
|
|
||
| /** Internal telemetry constants shared between OpenTelemetry tracing and metrics. */ | ||
| @InternalApi | ||
| public class TelemetryConstants { | ||
| static final String SERVICE_NAME = "datastore.googleapis.com"; | ||
| static final String METER_NAME = "com.google.cloud.datastore"; | ||
|
|
||
| public static final String ATTRIBUTES_KEY_DOCUMENT_COUNT = "doc_count"; | ||
| public static final String ATTRIBUTES_KEY_TRANSACTIONAL = "transactional"; | ||
| public static final String ATTRIBUTES_KEY_TRANSACTION_ID = "transaction_id"; | ||
| public static final String ATTRIBUTES_KEY_READ_CONSISTENCY = "read_consistency"; | ||
| public static final String ATTRIBUTES_KEY_RECEIVED = "Received"; | ||
| public static final String ATTRIBUTES_KEY_MISSING = "Missing"; | ||
| public static final String ATTRIBUTES_KEY_DEFERRED = "Deferred"; | ||
| public static final String ATTRIBUTES_KEY_MORE_RESULTS = "more_results"; | ||
|
|
||
| /* TODO(lawrenceqiu): For now, these are a duplicate of method names in TraceUtil. Those will use these eventually */ | ||
| public static final String METHOD_ALLOCATE_IDS = "AllocateIds"; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I noticed that these are PascalCase, while others are lowercase (e.g. "add", "put"). Are there plans to standardize them?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, this isn't consistent and I think ideally it should be in pascalcase. These constants we pulled from the existing TraceUtil file. I don't want to change this since there may be customers who are currently using those traces. |
||
| public static final String METHOD_BEGIN_TRANSACTION = "Transaction.Begin"; | ||
| public static final String METHOD_COMMIT = "Commit"; | ||
| public static final String METHOD_LOOKUP = "Lookup"; | ||
| public static final String METHOD_RESERVE_IDS = "ReserveIds"; | ||
| public static final String METHOD_RUN_QUERY = "RunQuery"; | ||
| public static final String METHOD_TRANSACTION_COMMIT = "Transaction.Commit"; | ||
| public static final String METHOD_TRANSACTION_LOOKUP = "Transaction.Lookup"; | ||
| public static final String METHOD_TRANSACTION_RUN = "Transaction.Run"; | ||
| public static final String METHOD_TRANSACTION_RUN_QUERY = "Transaction.RunQuery"; | ||
| public static final String METHOD_TRANSACTION_ROLLBACK = "Transaction.Rollback"; | ||
| public static final String METHOD_TRANSACTION_RUN_AGGREGATION_QUERY = | ||
| "Transaction.RunAggregationQuery"; | ||
| public static final String METHOD_ADD = "add"; | ||
| public static final String METHOD_PUT = "put"; | ||
| public static final String METHOD_UPDATE = "update"; | ||
| public static final String METHOD_DELETE = "delete"; | ||
| public static final String METHOD_SUBMIT = "submit"; | ||
|
|
||
| private TelemetryConstants() {} | ||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need
/internal/clientprefixes?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not 100% sure about this long-term. I think I would need to run this by the platform team. For now, since it's not being sent, I think this prefix should be fine. Since we're not promoting this yet, I think we should be able to change this in the future.