Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@

package com.google.cloud.datastore;

import static com.google.cloud.datastore.telemetry.TraceUtil.ATTRIBUTES_KEY_DEFERRED;
import static com.google.cloud.datastore.telemetry.TraceUtil.ATTRIBUTES_KEY_DOCUMENT_COUNT;
import static com.google.cloud.datastore.telemetry.TraceUtil.ATTRIBUTES_KEY_MISSING;
import static com.google.cloud.datastore.telemetry.TraceUtil.ATTRIBUTES_KEY_MORE_RESULTS;
import static com.google.cloud.datastore.telemetry.TraceUtil.ATTRIBUTES_KEY_READ_CONSISTENCY;
import static com.google.cloud.datastore.telemetry.TraceUtil.ATTRIBUTES_KEY_RECEIVED;
import static com.google.cloud.datastore.telemetry.TraceUtil.ATTRIBUTES_KEY_TRANSACTIONAL;
import static com.google.cloud.datastore.telemetry.TraceUtil.ATTRIBUTES_KEY_TRANSACTION_ID;
import static com.google.cloud.datastore.telemetry.TelemetryConstants.ATTRIBUTES_KEY_DEFERRED;
import static com.google.cloud.datastore.telemetry.TelemetryConstants.ATTRIBUTES_KEY_DOCUMENT_COUNT;
import static com.google.cloud.datastore.telemetry.TelemetryConstants.ATTRIBUTES_KEY_MISSING;
import static com.google.cloud.datastore.telemetry.TelemetryConstants.ATTRIBUTES_KEY_MORE_RESULTS;
import static com.google.cloud.datastore.telemetry.TelemetryConstants.ATTRIBUTES_KEY_READ_CONSISTENCY;
import static com.google.cloud.datastore.telemetry.TelemetryConstants.ATTRIBUTES_KEY_RECEIVED;
import static com.google.cloud.datastore.telemetry.TelemetryConstants.ATTRIBUTES_KEY_TRANSACTIONAL;
import static com.google.cloud.datastore.telemetry.TelemetryConstants.ATTRIBUTES_KEY_TRANSACTION_ID;
import static com.google.cloud.datastore.telemetry.TraceUtil.SPAN_NAME_ALLOCATE_IDS;
import static com.google.cloud.datastore.telemetry.TraceUtil.SPAN_NAME_BEGIN_TRANSACTION;
import static com.google.cloud.datastore.telemetry.TraceUtil.SPAN_NAME_COMMIT;
Expand Down
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";
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() {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,6 @@ public interface TraceUtil {
String SPAN_NAME_TRANSACTION_RUN_QUERY = "Transaction.RunQuery";
String SPAN_NAME_ROLLBACK = "Transaction.Rollback";
String SPAN_NAME_TRANSACTION_RUN_AGGREGATION_QUERY = "Transaction.RunAggregationQuery";
String ATTRIBUTES_KEY_DOCUMENT_COUNT = "doc_count";
String ATTRIBUTES_KEY_TRANSACTIONAL = "transactional";
String ATTRIBUTES_KEY_TRANSACTION_ID = "transaction_id";
String ATTRIBUTES_KEY_READ_CONSISTENCY = "read_consistency";
String ATTRIBUTES_KEY_RECEIVED = "Received";
String ATTRIBUTES_KEY_MISSING = "Missing";
String ATTRIBUTES_KEY_DEFERRED = "Deferred";
String ATTRIBUTES_KEY_MORE_RESULTS = "more_results";

/**
* Creates and returns an instance of the TraceUtil class.
Expand Down
Loading
Loading