From 30a7011130472e24dc65680e12174781bf0e5376 Mon Sep 17 00:00:00 2001
From: milaGGL <107142260+milaGGL@users.noreply.github.com>
Date: Wed, 14 Aug 2024 17:09:34 -0400
Subject: [PATCH 001/103] enable 4 metrics from GAX
---
google-cloud-firestore/pom.xml | 28 +--
.../FirestoreOpenTelemetryOptions.java | 21 +++
.../cloud/firestore/FirestoreOptions.java | 8 +
.../firestore/spi/v1/GrpcFirestoreRpc.java | 9 +
.../firestore/telemetry/MetricsUtil.java | 168 ++++++++++++++++++
5 files changed, 222 insertions(+), 12 deletions(-)
create mode 100644 google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java
diff --git a/google-cloud-firestore/pom.xml b/google-cloud-firestore/pom.xml
index 48c1ea9ec..d38ac2a43 100644
--- a/google-cloud-firestore/pom.xml
+++ b/google-cloud-firestore/pom.xml
@@ -119,6 +119,21 @@
io.opentelemetry.instrumentation
opentelemetry-grpc-1.6
+
+ io.opentelemetry
+ opentelemetry-sdk
+ ${opentelemetry.version}
+
+
+ com.google.cloud.opentelemetry
+ exporter-metrics
+ 0.31.0
+
+
+ io.opentelemetry
+ opentelemetry-sdk-common
+ ${opentelemetry.version}
+
@@ -181,12 +196,6 @@
test
-
- io.opentelemetry
- opentelemetry-sdk
- ${opentelemetry.version}
- test
-
io.opentelemetry
opentelemetry-sdk-testing
@@ -205,12 +214,7 @@
${opentelemetry.version}
test
-
- io.opentelemetry
- opentelemetry-sdk-common
- ${opentelemetry.version}
- test
-
+
com.google.cloud.opentelemetry
exporter-trace
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java
index 2b4606565..752b9480a 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOpenTelemetryOptions.java
@@ -28,10 +28,12 @@
@BetaApi
public class FirestoreOpenTelemetryOptions {
private final boolean tracingEnabled;
+ private final boolean metricsEnabled;
private final @Nullable OpenTelemetry openTelemetry;
FirestoreOpenTelemetryOptions(Builder builder) {
this.tracingEnabled = builder.tracingEnabled;
+ this.metricsEnabled = builder.metricsEnabled;
this.openTelemetry = builder.openTelemetry;
}
@@ -39,6 +41,10 @@ public boolean isTracingEnabled() {
return tracingEnabled;
}
+ public boolean isMetricsEnabled() {
+ return metricsEnabled;
+ }
+
public OpenTelemetry getOpenTelemetry() {
return openTelemetry;
}
@@ -57,15 +63,19 @@ public static class Builder {
private boolean tracingEnabled;
+ private boolean metricsEnabled;
+
@Nullable private OpenTelemetry openTelemetry;
private Builder() {
tracingEnabled = false;
+ metricsEnabled = false;
openTelemetry = null;
}
private Builder(FirestoreOpenTelemetryOptions options) {
this.tracingEnabled = options.tracingEnabled;
+ this.metricsEnabled = options.metricsEnabled;
this.openTelemetry = options.openTelemetry;
}
@@ -85,6 +95,17 @@ public FirestoreOpenTelemetryOptions.Builder setTracingEnabled(boolean tracingEn
return this;
}
+ /**
+ * Sets whether client side metrics should be enabled.
+ *
+ * @param metricsEnabled Whether client side metrics should be enabled.
+ */
+ @Nonnull
+ public FirestoreOpenTelemetryOptions.Builder setMetricsEnabled(boolean metricsEnabled) {
+ this.metricsEnabled = metricsEnabled;
+ return this;
+ }
+
/**
* Sets the {@link OpenTelemetry} to use with this Firestore instance. If telemetry collection
* is enabled, but an `OpenTelemetry` is not provided, the Firestore SDK will attempt to use the
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java
index b80b9dacd..259082a3f 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java
@@ -30,6 +30,7 @@
import com.google.cloud.TransportOptions;
import com.google.cloud.firestore.spi.v1.FirestoreRpc;
import com.google.cloud.firestore.spi.v1.GrpcFirestoreRpc;
+import com.google.cloud.firestore.telemetry.MetricsUtil;
import com.google.cloud.firestore.v1.FirestoreSettings;
import com.google.cloud.grpc.GrpcTransportOptions;
import com.google.common.collect.ImmutableMap;
@@ -64,6 +65,7 @@ public final class FirestoreOptions extends ServiceOptions PROJECT_ID_KEY = AttributeKey.stringKey("project_id");
+ // public static final AttributeKey INSTANCE_ID_KEY = AttributeKey.stringKey("instance");
+
+ // Metric attribute keys for labels
+ static final AttributeKey LANGUAGE_KEY = AttributeKey.stringKey("language");
+ static final AttributeKey METHOD_NAME_KEY = AttributeKey.stringKey("method_name");
+ static final AttributeKey STATUS_KEY = AttributeKey.stringKey("status");
+ public static final AttributeKey DATABASE_ID_KEY = AttributeKey.stringKey("database_id");
+
+ static final AttributeKey CLIENT_UID_KEY = AttributeKey.stringKey("client_uid");
+
+ // Metric
+ static final String OPERATION_LATENCIES_NAME = "operation_latencies";
+ static final String OPERATION_COUNT_NAME = "operation_count";
+ static final String ATTEMPT_LATENCIES_NAME = "attempt_latencies";
+ static final String ATTEMPT_COUNT_NAME = "attempt_count";
+ static final String FIRST_RESPONSE_LATENCIES_NAME = "first_response_latencies";
+ static final String END_TO_END_LATENCIES_NAME = "end_to_end_latencies";
+ static final String TRANSACTION_LATENCIES_NAME = "transaction_latencies";
+ static final String TRANSACTION_ATTEMPT_COUNT_NAME = "transaction_attempt_count";
+
+ static final String MILLISECOND_UNIT = "ms";
+
+ static final String ENABLE_METRICS_ENV_VAR = "FIRESTORE_ENABLE_TRACING";
+
+ private final FirestoreOptions firestoreOptions;
+ private OpenTelemetry openTelemetry;
+ private ApiTracerFactory otelApiTracerFactory;
+
+ private Meter meter;
+ private DoubleHistogram endToEndRequestLatency;
+ private DoubleHistogram firstResponseLatency;
+
+ /**
+ * Creates and returns an instance of the MetricsUtil class.
+ *
+ * @param firestoreOptions The FirestoreOptions object that is requesting an instance of
+ * MetricsUtil.
+ * @return An instance of the MetricsUtil class.
+ */
+ public MetricsUtil(FirestoreOptions firestoreOptions) {
+ this.firestoreOptions = firestoreOptions;
+
+ boolean createEnabledInstance = firestoreOptions.getOpenTelemetryOptions().isMetricsEnabled();
+
+ // The environment variable can override options to enable/disable telemetry collection.
+ String enableMetricsEnvVar = System.getenv(ENABLE_METRICS_ENV_VAR);
+ if (enableMetricsEnvVar != null) {
+ if (enableMetricsEnvVar.equalsIgnoreCase("true")
+ || enableMetricsEnvVar.equalsIgnoreCase("on")) {
+ createEnabledInstance = true;
+ }
+ if (enableMetricsEnvVar.equalsIgnoreCase("false")
+ || enableMetricsEnvVar.equalsIgnoreCase("off")) {
+ createEnabledInstance = false;
+ }
+ }
+
+ if (createEnabledInstance) {
+ createMtericsUtil();
+ }
+ }
+
+ private void createMtericsUtil() {
+ this.openTelemetry = getDefaultOpenTelemetryInstance();
+
+ OpenTelemetryMetricsRecorder recorder =
+ new OpenTelemetryMetricsRecorder(openTelemetry, METRICS_INTERNAL);
+
+ this.otelApiTracerFactory = new MetricsTracerFactory(recorder);
+
+ registerMetrics();
+ }
+
+ void registerMetrics() {
+ Package pkg = this.getClass().getPackage();
+ this.meter =
+ openTelemetry
+ .meterBuilder(FIRESTORE_LIBRARY_NAME)
+ .setInstrumentationVersion(pkg.getImplementationVersion())
+ .build();
+
+ this.endToEndRequestLatency =
+ meter
+ .histogramBuilder(METRICS_INTERNAL+"/"+END_TO_END_LATENCIES_NAME)
+ .setDescription("Firestore E2E metrics")
+ .setUnit(MILLISECOND_UNIT)
+ .build();
+
+ this.firstResponseLatency =
+ meter
+ .histogramBuilder(METRICS_INTERNAL+"/"+FIRST_RESPONSE_LATENCIES_NAME)
+ .setDescription("Firestore query first response latency")
+ .setUnit(MILLISECOND_UNIT)
+ .build();
+ }
+
+ private OpenTelemetry getDefaultOpenTelemetryInstance() {
+ OpenTelemetry openTelemetry = firestoreOptions.getOpenTelemetryOptions().getOpenTelemetry();
+
+ // If metrics is enabled, but an OpenTelemetry instance is not provided, create a default OTel
+ // instance.
+ if (openTelemetry == null) {
+ MetricExporter metricExporter =
+ GoogleCloudMetricExporter.createWithConfiguration(
+ MetricConfiguration.builder()
+ .setProjectId(firestoreOptions.getProjectId())
+ .setPrefix(METRICS_NAMESPACE)
+ .build());
+
+ SdkMeterProvider METER_PROVIDER =
+ SdkMeterProvider.builder()
+ .registerMetricReader(PeriodicMetricReader.builder(metricExporter).build())
+ .build();
+
+ openTelemetry = OpenTelemetrySdk.builder().setMeterProvider(METER_PROVIDER).build();
+ }
+ return openTelemetry;
+ }
+
+ public ApiTracerFactory getOpenTelemetryApiTracerFactory() {
+ return this.otelApiTracerFactory;
+ }
+}
From ed85413cce957cb4db62c01938dc84f22e3c7d55 Mon Sep 17 00:00:00 2001
From: milaGGL <107142260+milaGGL@users.noreply.github.com>
Date: Fri, 16 Aug 2024 12:11:28 -0400
Subject: [PATCH 002/103] format
---
.../com/google/cloud/firestore/spi/v1/GrpcFirestoreRpc.java | 5 ++---
.../com/google/cloud/firestore/telemetry/MetricsUtil.java | 6 +++---
2 files changed, 5 insertions(+), 6 deletions(-)
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1/GrpcFirestoreRpc.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1/GrpcFirestoreRpc.java
index 49359d598..9ddc4998b 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1/GrpcFirestoreRpc.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1/GrpcFirestoreRpc.java
@@ -28,8 +28,6 @@
import com.google.api.gax.rpc.ServerStreamingCallable;
import com.google.api.gax.rpc.TransportChannel;
import com.google.api.gax.rpc.UnaryCallable;
-import com.google.api.gax.tracing.MetricsTracerFactory;
-import com.google.api.gax.tracing.OpenTelemetryMetricsRecorder;
import com.google.cloud.NoCredentials;
import com.google.cloud.ServiceOptions;
import com.google.cloud.firestore.FirestoreOptions;
@@ -163,7 +161,8 @@ public GrpcFirestoreRpc(final FirestoreOptions options) throws IOException {
firestoreBuilder.setTracerFactory(options.getApiTracerFactory());
} else if (options.getOpenTelemetryOptions().isMetricsEnabled()) {
// TODO: move this logic to Firestore options
- firestoreBuilder.setTracerFactory(options.getMetricsUtil().getOpenTelemetryApiTracerFactory());
+ firestoreBuilder.setTracerFactory(
+ options.getMetricsUtil().getOpenTelemetryApiTracerFactory());
}
firestoreStub = GrpcFirestoreStub.create(firestoreBuilder.build());
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java
index 4a638d9a6..d3e2364fe 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java
@@ -112,7 +112,7 @@ private void createMtericsUtil() {
new OpenTelemetryMetricsRecorder(openTelemetry, METRICS_INTERNAL);
this.otelApiTracerFactory = new MetricsTracerFactory(recorder);
-
+
registerMetrics();
}
@@ -126,14 +126,14 @@ void registerMetrics() {
this.endToEndRequestLatency =
meter
- .histogramBuilder(METRICS_INTERNAL+"/"+END_TO_END_LATENCIES_NAME)
+ .histogramBuilder(METRICS_INTERNAL + "/" + END_TO_END_LATENCIES_NAME)
.setDescription("Firestore E2E metrics")
.setUnit(MILLISECOND_UNIT)
.build();
this.firstResponseLatency =
meter
- .histogramBuilder(METRICS_INTERNAL+"/"+FIRST_RESPONSE_LATENCIES_NAME)
+ .histogramBuilder(METRICS_INTERNAL + "/" + FIRST_RESPONSE_LATENCIES_NAME)
.setDescription("Firestore query first response latency")
.setUnit(MILLISECOND_UNIT)
.build();
From 57084c4b0b3a87a045f0777553a2efd25fe69603 Mon Sep 17 00:00:00 2001
From: milaGGL <107142260+milaGGL@users.noreply.github.com>
Date: Wed, 21 Aug 2024 11:33:54 -0400
Subject: [PATCH 003/103] add default otel
---
.../cloud/firestore/DocumentReference.java | 8 +
.../google/cloud/firestore/FirestoreImpl.java | 24 ++
.../com/google/cloud/firestore/Query.java | 17 ++
.../firestore/telemetry/MetricsUtil.java | 248 ++++++++++++++++--
4 files changed, 269 insertions(+), 28 deletions(-)
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java
index 57254bb2b..bbad025e7 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java
@@ -21,11 +21,13 @@
import com.google.api.core.InternalExtensionOnly;
import com.google.api.gax.rpc.ApiException;
import com.google.api.gax.rpc.ApiExceptions;
+import com.google.cloud.firestore.telemetry.MetricsUtil;
import com.google.cloud.firestore.telemetry.TraceUtil;
import com.google.cloud.firestore.telemetry.TraceUtil.Scope;
import com.google.cloud.firestore.v1.FirestoreClient.ListCollectionIdsPagedResponse;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.firestore.v1.ListCollectionIdsRequest;
+import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -473,8 +475,14 @@ public ApiFuture delete() {
@Nonnull
public ApiFuture get() {
TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_GET);
+
+ MetricsUtil util = getFirestore().getOptions().getMetricsUtil();
+ double start = System.currentTimeMillis();
try (Scope ignored = span.makeCurrent()) {
ApiFuture result = extractFirst(rpcContext.getFirestore().getAll(this));
+ Map attributes = new HashMap<>();
+ attributes.put("method_name", "DocumentReference.get");
+ util.endAtFuture(result, start, attributes);
span.endAtFuture(result);
return result;
} catch (Exception error) {
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java
index 48c691466..dcf8a18cc 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java
@@ -32,6 +32,7 @@
import com.google.api.gax.rpc.UnaryCallable;
import com.google.cloud.Timestamp;
import com.google.cloud.firestore.spi.v1.FirestoreRpc;
+import com.google.cloud.firestore.telemetry.MetricsUtil;
import com.google.cloud.firestore.telemetry.TraceUtil;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
@@ -227,6 +228,12 @@ void getAll(
// that we receive from the server.
final int NUM_RESPONSES_PER_TRACE_EVENT = 100;
+ MetricsUtil util = getFirestore().getOptions().getMetricsUtil();
+ double start = System.currentTimeMillis();
+ Map attributes = new HashMap<>();
+ String method = transactionId != null ? "Batch.get" : "Transaction.get";
+ attributes.put("method_name", method);
+
ResponseObserver responseObserver =
new ResponseObserver() {
int numResponses = 0;
@@ -254,6 +261,12 @@ public void onResponse(BatchGetDocumentsResponse response) {
getTraceUtil()
.currentSpan()
.addEvent(TraceUtil.SPAN_NAME_BATCH_GET_DOCUMENTS + ": First response received");
+
+ double end = System.currentTimeMillis();
+ double elapsedTime = end - start;
+ attributes.put("status", "OK");
+ util.firstResponseLatencyRecorder(elapsedTime, attributes);
+
} else if (numResponses % NUM_RESPONSES_PER_TRACE_EVENT == 0) {
getTraceUtil()
.currentSpan()
@@ -262,6 +275,11 @@ public void onResponse(BatchGetDocumentsResponse response) {
+ ": Received "
+ numResponses
+ " responses");
+
+ double end = System.currentTimeMillis();
+ double elapsedTime = end - start;
+ attributes.put("status", "OK");
+ util.endToEndRequestLatencyRecorder(elapsedTime, attributes);
}
switch (response.getResultCase()) {
@@ -298,6 +316,12 @@ public void onResponse(BatchGetDocumentsResponse response) {
@Override
public void onError(Throwable throwable) {
getTraceUtil().currentSpan().end(throwable);
+
+ double end = System.currentTimeMillis();
+ double elapsedTime = end - start;
+ attributes.put("status", MetricsUtil.extractErrorStatus(throwable));
+ util.endToEndRequestLatencyRecorder(elapsedTime, attributes);
+
apiStreamObserver.onError(throwable);
}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java
index 4721ba93d..58431de14 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java
@@ -39,6 +39,7 @@
import com.google.auto.value.AutoValue;
import com.google.cloud.Timestamp;
import com.google.cloud.firestore.Query.QueryOptions.Builder;
+import com.google.cloud.firestore.telemetry.MetricsUtil;
import com.google.cloud.firestore.telemetry.TraceUtil;
import com.google.cloud.firestore.telemetry.TraceUtil.Scope;
import com.google.cloud.firestore.v1.FirestoreSettings;
@@ -65,9 +66,11 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
+import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.SortedSet;
@@ -1740,6 +1743,12 @@ private void internalStream(
final AtomicReference lastReceivedDocument = new AtomicReference<>();
+ MetricsUtil util = getFirestore().getOptions().getMetricsUtil();
+ long start = System.currentTimeMillis();
+ Map attributes = new HashMap<>();
+ String method = transactionId != null ? "Query.get" : "Transaction.get";
+ attributes.put("method_name", method);
+
ResponseObserver observer =
new ResponseObserver() {
Timestamp readTime;
@@ -1967,6 +1976,12 @@ ApiFuture get(
transactionId == null
? TraceUtil.SPAN_NAME_QUERY_GET
: TraceUtil.SPAN_NAME_TRANSACTION_GET_QUERY);
+
+ MetricsUtil util = getFirestore().getOptions().getMetricsUtil();
+ long start = System.currentTimeMillis();
+ Map attributes = new HashMap<>();
+ String method = transactionId != null ? "Query.get" : "Transaction.get";
+ attributes.put("method_name", method);
try (Scope ignored = span.makeCurrent()) {
final SettableApiFuture result = SettableApiFuture.create();
internalStream(
@@ -2013,6 +2028,8 @@ public void onCompleted() {
/* isRetryRequestWithCursor= */ false);
span.endAtFuture(result);
+ util.endAtFuture(result, start, attributes);
+
return result;
} catch (Exception error) {
span.end(error);
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java
index d3e2364fe..3f9b44f82 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java
@@ -16,20 +16,40 @@
package com.google.cloud.firestore.telemetry;
+import com.google.api.core.ApiFuture;
+import com.google.api.core.ApiFutureCallback;
+import com.google.api.core.ApiFutures;
+import com.google.api.gax.rpc.StatusCode;
import com.google.api.gax.tracing.ApiTracerFactory;
import com.google.api.gax.tracing.MetricsTracerFactory;
import com.google.api.gax.tracing.OpenTelemetryMetricsRecorder;
+import com.google.cloud.firestore.FirestoreException;
import com.google.cloud.firestore.FirestoreOptions;
import com.google.cloud.opentelemetry.metric.GoogleCloudMetricExporter;
import com.google.cloud.opentelemetry.metric.MetricConfiguration;
+import com.google.common.annotations.VisibleForTesting;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import com.google.common.util.concurrent.MoreExecutors;
+import io.grpc.Status;
import io.opentelemetry.api.OpenTelemetry;
import io.opentelemetry.api.common.AttributeKey;
+import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.api.metrics.DoubleHistogram;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.sdk.OpenTelemetrySdk;
+import io.opentelemetry.sdk.metrics.InstrumentSelector;
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
+import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder;
+import io.opentelemetry.sdk.metrics.View;
import io.opentelemetry.sdk.metrics.export.MetricExporter;
import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader;
+import io.opentelemetry.sdk.resources.Resource;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
/**
* A utility interface for trace collection. Classes that implement this interface may make their
@@ -37,36 +57,64 @@
* they may use a particular tracing framework such as OpenTelemetry.
*/
public class MetricsUtil {
- static final String FIRESTORE_LIBRARY_NAME = "firestore_java";
- static final String METRICS_NAMESPACE = "custom.googleapis.com";
- public static final String METRICS_INTERNAL = "internal";
+ static final String FIRESTORE_METER_NAME = "firestore_java";
+
+ // TODO: change to firestore.googleapis.com
+ public static final String METER_NAME = "custom.googleapis.com/internal/client";
+ public static final String GAX_METER_NAME = OpenTelemetryMetricsRecorder.GAX_METER_NAME;
// Metric attribute keys for monitored resource
static final AttributeKey PROJECT_ID_KEY = AttributeKey.stringKey("project_id");
// public static final AttributeKey INSTANCE_ID_KEY = AttributeKey.stringKey("instance");
// Metric attribute keys for labels
- static final AttributeKey LANGUAGE_KEY = AttributeKey.stringKey("language");
+ // static final AttributeKey LANGUAGE_KEY = AttributeKey.stringKey("language");
static final AttributeKey METHOD_NAME_KEY = AttributeKey.stringKey("method_name");
static final AttributeKey STATUS_KEY = AttributeKey.stringKey("status");
public static final AttributeKey DATABASE_ID_KEY = AttributeKey.stringKey("database_id");
-
+ static final AttributeKey CLIENT_LIBRARY_KEY = AttributeKey.stringKey("client_library");
+ static final AttributeKey LIBRARY_VERSION_KEY = AttributeKey.stringKey("library_version");
static final AttributeKey CLIENT_UID_KEY = AttributeKey.stringKey("client_uid");
+ static final AttributeKey Language_KEY = AttributeKey.stringKey("language");
+ public static final AttributeKey SERVICE_NAME = AttributeKey.stringKey("service.name");
// Metric
- static final String OPERATION_LATENCIES_NAME = "operation_latencies";
+ static final String OPERATION_LATENCY_NAME = "operation_latency";
static final String OPERATION_COUNT_NAME = "operation_count";
- static final String ATTEMPT_LATENCIES_NAME = "attempt_latencies";
+ static final String ATTEMPT_LATENCY_NAME = "attempt_latency";
static final String ATTEMPT_COUNT_NAME = "attempt_count";
- static final String FIRST_RESPONSE_LATENCIES_NAME = "first_response_latencies";
- static final String END_TO_END_LATENCIES_NAME = "end_to_end_latencies";
- static final String TRANSACTION_LATENCIES_NAME = "transaction_latencies";
+ static final String FIRST_RESPONSE_LATENCY_NAME = "first_response_latency";
+ static final String END_TO_END_LATENCY_NAME = "end_to_end_latency";
+ static final String TRANSACTION_LATENCY_NAME = "transaction_latency";
static final String TRANSACTION_ATTEMPT_COUNT_NAME = "transaction_attempt_count";
static final String MILLISECOND_UNIT = "ms";
static final String ENABLE_METRICS_ENV_VAR = "FIRESTORE_ENABLE_TRACING";
+ public static final Set COMMON_ATTRIBUTES =
+ ImmutableSet.of(
+ PROJECT_ID_KEY,
+ DATABASE_ID_KEY,
+ CLIENT_UID_KEY,
+ STATUS_KEY,
+ CLIENT_LIBRARY_KEY,
+ LIBRARY_VERSION_KEY);
+
+ public static final Set BUILTIN_METRICS =
+ ImmutableSet.of(
+ OPERATION_LATENCY_NAME,
+ ATTEMPT_LATENCY_NAME,
+ OPERATION_COUNT_NAME,
+ ATTEMPT_COUNT_NAME,
+ FIRST_RESPONSE_LATENCY_NAME,
+ END_TO_END_LATENCY_NAME,
+ TRANSACTION_LATENCY_NAME,
+ TRANSACTION_ATTEMPT_COUNT_NAME)
+ .stream()
+ .map(m -> METER_NAME + '/' + m)
+ .collect(Collectors.toSet());
+
private final FirestoreOptions firestoreOptions;
private OpenTelemetry openTelemetry;
private ApiTracerFactory otelApiTracerFactory;
@@ -101,15 +149,15 @@ public MetricsUtil(FirestoreOptions firestoreOptions) {
}
if (createEnabledInstance) {
- createMtericsUtil();
+ createMetricsUtil();
}
}
- private void createMtericsUtil() {
+ private void createMetricsUtil() {
this.openTelemetry = getDefaultOpenTelemetryInstance();
OpenTelemetryMetricsRecorder recorder =
- new OpenTelemetryMetricsRecorder(openTelemetry, METRICS_INTERNAL);
+ new OpenTelemetryMetricsRecorder(openTelemetry, METER_NAME);
this.otelApiTracerFactory = new MetricsTracerFactory(recorder);
@@ -117,28 +165,35 @@ private void createMtericsUtil() {
}
void registerMetrics() {
- Package pkg = this.getClass().getPackage();
- this.meter =
- openTelemetry
- .meterBuilder(FIRESTORE_LIBRARY_NAME)
- .setInstrumentationVersion(pkg.getImplementationVersion())
- .build();
+ this.meter = openTelemetry.getMeter(FIRESTORE_METER_NAME);
this.endToEndRequestLatency =
meter
- .histogramBuilder(METRICS_INTERNAL + "/" + END_TO_END_LATENCIES_NAME)
+ .histogramBuilder(METER_NAME + "/" + END_TO_END_LATENCY_NAME)
.setDescription("Firestore E2E metrics")
.setUnit(MILLISECOND_UNIT)
.build();
this.firstResponseLatency =
meter
- .histogramBuilder(METRICS_INTERNAL + "/" + FIRST_RESPONSE_LATENCIES_NAME)
+ .histogramBuilder(METER_NAME + "/" + FIRST_RESPONSE_LATENCY_NAME)
.setDescription("Firestore query first response latency")
.setUnit(MILLISECOND_UNIT)
.build();
}
+ public void endToEndRequestLatencyRecorder(double latency, Map attributes) {
+ if (endToEndRequestLatency != null) {
+ endToEndRequestLatency.record(latency, toOtelAttributes(attributes));
+ }
+ }
+
+ public void firstResponseLatencyRecorder(double latency, Map attributes) {
+ if (firstResponseLatency != null) {
+ firstResponseLatency.record(latency, toOtelAttributes(attributes));
+ }
+ }
+
private OpenTelemetry getDefaultOpenTelemetryInstance() {
OpenTelemetry openTelemetry = firestoreOptions.getOpenTelemetryOptions().getOpenTelemetry();
@@ -147,17 +202,32 @@ private OpenTelemetry getDefaultOpenTelemetryInstance() {
if (openTelemetry == null) {
MetricExporter metricExporter =
GoogleCloudMetricExporter.createWithConfiguration(
- MetricConfiguration.builder()
- .setProjectId(firestoreOptions.getProjectId())
- .setPrefix(METRICS_NAMESPACE)
- .build());
+ MetricConfiguration.builder().setProjectId(firestoreOptions.getProjectId()).build());
+
+ SdkMeterProviderBuilder sdkMeterProviderBuilder = SdkMeterProvider.builder();
+
+ // Remove "Language" attributes from GAX metrics
+ for (Map.Entry entry : getAllViews().entrySet()) {
+ sdkMeterProviderBuilder.registerView(entry.getKey(), entry.getValue());
+ }
+
+ // This adds to Metrics.Resource.Attributes, doesn't show up on GC monitoring dashboard.
+ Package pkg = this.getClass().getPackage();
+ Resource resource =
+ Resource.getDefault()
+ .merge(
+ Resource.builder()
+ .put(CLIENT_LIBRARY_KEY, FIRESTORE_METER_NAME)
+ .put(LIBRARY_VERSION_KEY, pkg.getImplementationVersion())
+ .build());
+ sdkMeterProviderBuilder.setResource(resource);
- SdkMeterProvider METER_PROVIDER =
- SdkMeterProvider.builder()
+ SdkMeterProvider meterProvider =
+ sdkMeterProviderBuilder
.registerMetricReader(PeriodicMetricReader.builder(metricExporter).build())
.build();
- openTelemetry = OpenTelemetrySdk.builder().setMeterProvider(METER_PROVIDER).build();
+ openTelemetry = OpenTelemetrySdk.builder().setMeterProvider(meterProvider).build();
}
return openTelemetry;
}
@@ -165,4 +235,126 @@ private OpenTelemetry getDefaultOpenTelemetryInstance() {
public ApiTracerFactory getOpenTelemetryApiTracerFactory() {
return this.otelApiTracerFactory;
}
+
+ @VisibleForTesting
+ Attributes toOtelAttributes(Map attributes) {
+ AttributesBuilder attributesBuilder = Attributes.builder();
+ attributes.forEach(attributesBuilder::put);
+ return attributesBuilder.build();
+ }
+
+ public void endAtFuture(
+ ApiFuture futureValue, double start, Map attributes) {
+ ApiFutures.addCallback(
+ futureValue,
+ new ApiFutureCallback() {
+ @Override
+ public void onFailure(Throwable t) {
+ double end = System.currentTimeMillis();
+ double elapsedTime = end - start;
+ attributes.put("status", extractErrorStatus(t));
+ endToEndRequestLatencyRecorder(elapsedTime, attributes);
+ }
+
+ @Override
+ public void onSuccess(T result) {
+ double end = System.currentTimeMillis();
+ double elapsedTime = end - start;
+ attributes.put("status", "OK");
+ endToEndRequestLatencyRecorder(elapsedTime, attributes);
+ }
+ },
+ MoreExecutors.directExecutor());
+ }
+
+ public void end(Throwable t, double start, Map attributes) {
+ attributes.put("status", extractErrorStatus(t));
+ double end = System.currentTimeMillis();
+ double elapsedTime = end - start;
+ endToEndRequestLatencyRecorder(elapsedTime, attributes);
+ }
+
+ /** Function to extract the status of the error as a string */
+ public static String extractErrorStatus(@Nullable Throwable throwable) {
+ if (!(throwable instanceof FirestoreException)) {
+ return StatusCode.Code.UNKNOWN.toString();
+ }
+ Status status = ((FirestoreException) throwable).getStatus();
+
+ return status.getCode().name();
+ }
+
+ static void defineView(
+ ImmutableMap.Builder viewMap,
+ String MeterName,
+ String id,
+ Set attributes) {
+ InstrumentSelector selector =
+ InstrumentSelector.builder().setName(id).setMeterName(MeterName).build();
+ Set attributesFilter =
+ ImmutableSet.builder()
+ .addAll(attributes.stream().map(AttributeKey::getKey).collect(Collectors.toSet()))
+ .build();
+ View view = View.builder().setName(id).setAttributeFilter(attributesFilter).build();
+
+ viewMap.put(selector, view);
+ }
+
+ public static Map getAllViews() {
+ ImmutableMap.Builder views = ImmutableMap.builder();
+
+ defineView(
+ views,
+ GAX_METER_NAME,
+ OPERATION_LATENCY_NAME,
+ ImmutableSet.builder()
+ .addAll(COMMON_ATTRIBUTES)
+ .add(METHOD_NAME_KEY)
+ .build());
+ defineView(
+ views,
+ GAX_METER_NAME,
+ ATTEMPT_LATENCY_NAME,
+ ImmutableSet.builder()
+ .addAll(COMMON_ATTRIBUTES)
+ .add(METHOD_NAME_KEY)
+ .build());
+
+ defineView(
+ views,
+ GAX_METER_NAME,
+ OPERATION_COUNT_NAME,
+ ImmutableSet.builder()
+ .addAll(COMMON_ATTRIBUTES)
+ .add(METHOD_NAME_KEY)
+ .build());
+
+ defineView(
+ views,
+ GAX_METER_NAME,
+ ATTEMPT_COUNT_NAME,
+ ImmutableSet.builder()
+ .addAll(COMMON_ATTRIBUTES)
+ .add(METHOD_NAME_KEY)
+ .build());
+
+ defineView(
+ views,
+ FIRESTORE_METER_NAME,
+ FIRST_RESPONSE_LATENCY_NAME,
+ ImmutableSet.builder()
+ .addAll(COMMON_ATTRIBUTES)
+ .add(METHOD_NAME_KEY)
+ .build());
+
+ defineView(
+ views,
+ FIRESTORE_METER_NAME,
+ END_TO_END_LATENCY_NAME,
+ ImmutableSet.builder()
+ .addAll(COMMON_ATTRIBUTES)
+ .add(METHOD_NAME_KEY)
+ .build());
+ return views.build();
+ }
}
From 571c0956aea852c122ed8149f46670df1fa2c7f5 Mon Sep 17 00:00:00 2001
From: milaGGL <107142260+milaGGL@users.noreply.github.com>
Date: Wed, 21 Aug 2024 14:28:13 -0400
Subject: [PATCH 004/103] format
---
.../firestore/telemetry/MetricsUtil.java | 47 ++++++++-----------
1 file changed, 20 insertions(+), 27 deletions(-)
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java
index 3f9b44f82..2bc1f7aaa 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java
@@ -97,9 +97,9 @@ public class MetricsUtil {
PROJECT_ID_KEY,
DATABASE_ID_KEY,
CLIENT_UID_KEY,
- STATUS_KEY,
CLIENT_LIBRARY_KEY,
- LIBRARY_VERSION_KEY);
+ LIBRARY_VERSION_KEY,
+ STATUS_KEY);
public static final Set BUILTIN_METRICS =
ImmutableSet.of(
@@ -200,35 +200,35 @@ private OpenTelemetry getDefaultOpenTelemetryInstance() {
// If metrics is enabled, but an OpenTelemetry instance is not provided, create a default OTel
// instance.
if (openTelemetry == null) {
+ SdkMeterProviderBuilder sdkMeterProviderBuilder = SdkMeterProvider.builder();
+
+ // Attach built-in exporter
MetricExporter metricExporter =
GoogleCloudMetricExporter.createWithConfiguration(
MetricConfiguration.builder().setProjectId(firestoreOptions.getProjectId()).build());
+ sdkMeterProviderBuilder.registerMetricReader(
+ PeriodicMetricReader.builder(metricExporter).build());
- SdkMeterProviderBuilder sdkMeterProviderBuilder = SdkMeterProvider.builder();
-
- // Remove "Language" attributes from GAX metrics
- for (Map.Entry entry : getAllViews().entrySet()) {
- sdkMeterProviderBuilder.registerView(entry.getKey(), entry.getValue());
- }
-
- // This adds to Metrics.Resource.Attributes, doesn't show up on GC monitoring dashboard.
+ // Add static attributes to resource
Package pkg = this.getClass().getPackage();
- Resource resource =
+ sdkMeterProviderBuilder.setResource(
Resource.getDefault()
.merge(
Resource.builder()
+ .put(DATABASE_ID_KEY, firestoreOptions.getDatabaseId())
.put(CLIENT_LIBRARY_KEY, FIRESTORE_METER_NAME)
.put(LIBRARY_VERSION_KEY, pkg.getImplementationVersion())
- .build());
- sdkMeterProviderBuilder.setResource(resource);
+ .build()));
- SdkMeterProvider meterProvider =
- sdkMeterProviderBuilder
- .registerMetricReader(PeriodicMetricReader.builder(metricExporter).build())
- .build();
+ // Filter out attributes that are not defined
+ for (Map.Entry entry : getAllViews().entrySet()) {
+ sdkMeterProviderBuilder.registerView(entry.getKey(), entry.getValue());
+ }
- openTelemetry = OpenTelemetrySdk.builder().setMeterProvider(meterProvider).build();
+ openTelemetry =
+ OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProviderBuilder.build()).build();
}
+
return openTelemetry;
}
@@ -286,16 +286,15 @@ public static String extractErrorStatus(@Nullable Throwable throwable) {
static void defineView(
ImmutableMap.Builder viewMap,
- String MeterName,
String id,
Set attributes) {
InstrumentSelector selector =
- InstrumentSelector.builder().setName(id).setMeterName(MeterName).build();
+ InstrumentSelector.builder().setName(METER_NAME + "/" + id).build();
Set attributesFilter =
ImmutableSet.builder()
.addAll(attributes.stream().map(AttributeKey::getKey).collect(Collectors.toSet()))
.build();
- View view = View.builder().setName(id).setAttributeFilter(attributesFilter).build();
+ View view = View.builder().setAttributeFilter(attributesFilter).build();
viewMap.put(selector, view);
}
@@ -305,7 +304,6 @@ public static Map getAllViews() {
defineView(
views,
- GAX_METER_NAME,
OPERATION_LATENCY_NAME,
ImmutableSet.builder()
.addAll(COMMON_ATTRIBUTES)
@@ -313,7 +311,6 @@ public static Map getAllViews() {
.build());
defineView(
views,
- GAX_METER_NAME,
ATTEMPT_LATENCY_NAME,
ImmutableSet.builder()
.addAll(COMMON_ATTRIBUTES)
@@ -322,7 +319,6 @@ public static Map getAllViews() {
defineView(
views,
- GAX_METER_NAME,
OPERATION_COUNT_NAME,
ImmutableSet.builder()
.addAll(COMMON_ATTRIBUTES)
@@ -331,7 +327,6 @@ public static Map getAllViews() {
defineView(
views,
- GAX_METER_NAME,
ATTEMPT_COUNT_NAME,
ImmutableSet.builder()
.addAll(COMMON_ATTRIBUTES)
@@ -340,7 +335,6 @@ public static Map getAllViews() {
defineView(
views,
- FIRESTORE_METER_NAME,
FIRST_RESPONSE_LATENCY_NAME,
ImmutableSet.builder()
.addAll(COMMON_ATTRIBUTES)
@@ -349,7 +343,6 @@ public static Map getAllViews() {
defineView(
views,
- FIRESTORE_METER_NAME,
END_TO_END_LATENCY_NAME,
ImmutableSet.builder()
.addAll(COMMON_ATTRIBUTES)
From f4b8416aed326d3290aaf6db01cd6cda920440c6 Mon Sep 17 00:00:00 2001
From: milaGGL <107142260+milaGGL@users.noreply.github.com>
Date: Thu, 22 Aug 2024 10:35:20 -0400
Subject: [PATCH 005/103] move constants out
---
.../telemetry/BuiltinMetricsConstants.java | 139 ++++++++++++++
.../telemetry/BuiltinMetricsView.java | 56 ++++++
.../firestore/telemetry/MetricsUtil.java | 173 ++----------------
3 files changed, 207 insertions(+), 161 deletions(-)
create mode 100644 google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java
create mode 100644 google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java
new file mode 100644
index 000000000..181802dc1
--- /dev/null
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java
@@ -0,0 +1,139 @@
+package com.google.cloud.firestore.telemetry;
+
+import com.google.api.gax.tracing.OpenTelemetryMetricsRecorder;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSet;
+import io.opentelemetry.api.common.AttributeKey;
+import io.opentelemetry.sdk.metrics.InstrumentSelector;
+import io.opentelemetry.sdk.metrics.View;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class BuiltinMetricsConstants {
+
+ static final String FIRESTORE_METER_NAME = "firestore_java";
+
+ // TODO: change to firestore.googleapis.com
+ public static final String METER_NAME = "custom.googleapis.com/internal/client";
+ public static final String GAX_METER_NAME = OpenTelemetryMetricsRecorder.GAX_METER_NAME;
+
+ // Metric attribute keys for monitored resource
+ static final AttributeKey PROJECT_ID_KEY = AttributeKey.stringKey("project_id");
+ // public static final AttributeKey INSTANCE_ID_KEY = AttributeKey.stringKey("instance");
+
+ // Metric attribute keys for labels
+ // static final AttributeKey LANGUAGE_KEY = AttributeKey.stringKey("language");
+ static final AttributeKey METHOD_NAME_KEY = AttributeKey.stringKey("method_name");
+ static final AttributeKey STATUS_KEY = AttributeKey.stringKey("status");
+ public static final AttributeKey DATABASE_ID_KEY = AttributeKey.stringKey("database_id");
+ static final AttributeKey CLIENT_LIBRARY_KEY = AttributeKey.stringKey("client_library");
+ static final AttributeKey LIBRARY_VERSION_KEY = AttributeKey.stringKey("library_version");
+ static final AttributeKey CLIENT_UID_KEY = AttributeKey.stringKey("client_uid");
+
+ static final AttributeKey Language_KEY = AttributeKey.stringKey("language");
+ public static final AttributeKey SERVICE_NAME = AttributeKey.stringKey("service.name");
+ // Metric
+ static final String OPERATION_LATENCY_NAME = "operation_latency";
+ static final String OPERATION_COUNT_NAME = "operation_count";
+ static final String ATTEMPT_LATENCY_NAME = "attempt_latency";
+ static final String ATTEMPT_COUNT_NAME = "attempt_count";
+ static final String FIRST_RESPONSE_LATENCY_NAME = "first_response_latency";
+ static final String END_TO_END_LATENCY_NAME = "end_to_end_latency";
+ static final String TRANSACTION_LATENCY_NAME = "transaction_latency";
+ static final String TRANSACTION_ATTEMPT_COUNT_NAME = "transaction_attempt_count";
+
+ static final String MILLISECOND_UNIT = "ms";
+
+ public static final String ENABLE_METRICS_ENV_VAR = "FIRESTORE_ENABLE_TRACING";
+
+ public static final Set COMMON_ATTRIBUTES =
+ ImmutableSet.of(
+ PROJECT_ID_KEY,
+ DATABASE_ID_KEY,
+ CLIENT_UID_KEY,
+ CLIENT_LIBRARY_KEY,
+ LIBRARY_VERSION_KEY,
+ STATUS_KEY);
+
+ public static final Set BUILTIN_METRICS =
+ ImmutableSet.of(
+ OPERATION_LATENCY_NAME,
+ ATTEMPT_LATENCY_NAME,
+ OPERATION_COUNT_NAME,
+ ATTEMPT_COUNT_NAME,
+ FIRST_RESPONSE_LATENCY_NAME,
+ END_TO_END_LATENCY_NAME,
+ TRANSACTION_LATENCY_NAME,
+ TRANSACTION_ATTEMPT_COUNT_NAME)
+ .stream()
+ .map(m -> METER_NAME + '/' + m)
+ .collect(Collectors.toSet());
+
+ static void defineView(
+ ImmutableMap.Builder viewMap,
+ String id,
+ Set attributes) {
+ InstrumentSelector selector =
+ InstrumentSelector.builder().setName(METER_NAME + "/" + id).build();
+ Set attributesFilter =
+ ImmutableSet.builder()
+ .addAll(attributes.stream().map(AttributeKey::getKey).collect(Collectors.toSet()))
+ .build();
+ View view = View.builder().setAttributeFilter(attributesFilter).build();
+
+ viewMap.put(selector, view);
+ }
+
+ public static Map getAllViews() {
+ ImmutableMap.Builder views = ImmutableMap.builder();
+
+ defineView(
+ views,
+ OPERATION_LATENCY_NAME,
+ ImmutableSet.builder()
+ .addAll(COMMON_ATTRIBUTES)
+ .add(METHOD_NAME_KEY)
+ .build());
+ defineView(
+ views,
+ ATTEMPT_LATENCY_NAME,
+ ImmutableSet.builder()
+ .addAll(COMMON_ATTRIBUTES)
+ .add(METHOD_NAME_KEY)
+ .build());
+
+ defineView(
+ views,
+ OPERATION_COUNT_NAME,
+ ImmutableSet.builder()
+ .addAll(COMMON_ATTRIBUTES)
+ .add(METHOD_NAME_KEY)
+ .build());
+
+ defineView(
+ views,
+ ATTEMPT_COUNT_NAME,
+ ImmutableSet.builder()
+ .addAll(COMMON_ATTRIBUTES)
+ .add(METHOD_NAME_KEY)
+ .build());
+
+ defineView(
+ views,
+ FIRST_RESPONSE_LATENCY_NAME,
+ ImmutableSet.builder()
+ .addAll(COMMON_ATTRIBUTES)
+ .add(METHOD_NAME_KEY)
+ .build());
+
+ defineView(
+ views,
+ END_TO_END_LATENCY_NAME,
+ ImmutableSet.builder()
+ .addAll(COMMON_ATTRIBUTES)
+ .add(METHOD_NAME_KEY)
+ .build());
+ return views.build();
+ }
+}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java
new file mode 100644
index 000000000..db9ebb6e4
--- /dev/null
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java
@@ -0,0 +1,56 @@
+package com.google.cloud.firestore.telemetry;
+
+/*
+ * Copyright 2023 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
+ *
+ * https://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.
+ */
+
+import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.*;
+
+import com.google.cloud.opentelemetry.metric.GoogleCloudMetricExporter;
+import com.google.cloud.opentelemetry.metric.MetricConfiguration;
+import io.opentelemetry.sdk.metrics.InstrumentSelector;
+import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder;
+import io.opentelemetry.sdk.metrics.View;
+import io.opentelemetry.sdk.metrics.export.MetricExporter;
+import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader;
+import java.io.IOException;
+import java.util.Map;
+
+public class BuiltinMetricsView {
+
+ private BuiltinMetricsView() {}
+
+ /** Register built-in metrics on the {@link SdkMeterProviderBuilder} with credentials. */
+ public static void registerBuiltinMetrics(String projectId, SdkMeterProviderBuilder builder)
+ throws IOException {
+
+ // Attach built-in exporter
+ MetricExporter metricExporter =
+ GoogleCloudMetricExporter.createWithConfiguration(
+ MetricConfiguration.builder().setProjectId(projectId).build());
+
+ builder.registerMetricReader(PeriodicMetricReader.builder(metricExporter).build());
+
+ // Add static attributes to resource
+ // Package pkg = this.getClass().getPackage();
+
+ // Filter out attributes that are not defined
+ for (Map.Entry entry : getAllViews().entrySet()) {
+ builder.registerView(entry.getKey(), entry.getValue());
+ }
+
+ builder.registerMetricReader(PeriodicMetricReader.create(metricExporter));
+ }
+}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java
index 2bc1f7aaa..e346d7c1a 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java
@@ -16,6 +16,8 @@
package com.google.cloud.firestore.telemetry;
+import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.*;
+
import com.google.api.core.ApiFuture;
import com.google.api.core.ApiFutureCallback;
import com.google.api.core.ApiFutures;
@@ -25,30 +27,19 @@
import com.google.api.gax.tracing.OpenTelemetryMetricsRecorder;
import com.google.cloud.firestore.FirestoreException;
import com.google.cloud.firestore.FirestoreOptions;
-import com.google.cloud.opentelemetry.metric.GoogleCloudMetricExporter;
-import com.google.cloud.opentelemetry.metric.MetricConfiguration;
import com.google.common.annotations.VisibleForTesting;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSet;
import com.google.common.util.concurrent.MoreExecutors;
import io.grpc.Status;
import io.opentelemetry.api.OpenTelemetry;
-import io.opentelemetry.api.common.AttributeKey;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.api.metrics.DoubleHistogram;
import io.opentelemetry.api.metrics.Meter;
import io.opentelemetry.sdk.OpenTelemetrySdk;
-import io.opentelemetry.sdk.metrics.InstrumentSelector;
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder;
-import io.opentelemetry.sdk.metrics.View;
-import io.opentelemetry.sdk.metrics.export.MetricExporter;
-import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader;
-import io.opentelemetry.sdk.resources.Resource;
+import java.io.IOException;
import java.util.Map;
-import java.util.Set;
-import java.util.stream.Collectors;
import javax.annotation.Nullable;
/**
@@ -57,63 +48,6 @@
* they may use a particular tracing framework such as OpenTelemetry.
*/
public class MetricsUtil {
- static final String FIRESTORE_METER_NAME = "firestore_java";
-
- // TODO: change to firestore.googleapis.com
- public static final String METER_NAME = "custom.googleapis.com/internal/client";
- public static final String GAX_METER_NAME = OpenTelemetryMetricsRecorder.GAX_METER_NAME;
-
- // Metric attribute keys for monitored resource
- static final AttributeKey PROJECT_ID_KEY = AttributeKey.stringKey("project_id");
- // public static final AttributeKey INSTANCE_ID_KEY = AttributeKey.stringKey("instance");
-
- // Metric attribute keys for labels
- // static final AttributeKey LANGUAGE_KEY = AttributeKey.stringKey("language");
- static final AttributeKey METHOD_NAME_KEY = AttributeKey.stringKey("method_name");
- static final AttributeKey STATUS_KEY = AttributeKey.stringKey("status");
- public static final AttributeKey DATABASE_ID_KEY = AttributeKey.stringKey("database_id");
- static final AttributeKey CLIENT_LIBRARY_KEY = AttributeKey.stringKey("client_library");
- static final AttributeKey LIBRARY_VERSION_KEY = AttributeKey.stringKey("library_version");
- static final AttributeKey CLIENT_UID_KEY = AttributeKey.stringKey("client_uid");
-
- static final AttributeKey Language_KEY = AttributeKey.stringKey("language");
- public static final AttributeKey SERVICE_NAME = AttributeKey.stringKey("service.name");
- // Metric
- static final String OPERATION_LATENCY_NAME = "operation_latency";
- static final String OPERATION_COUNT_NAME = "operation_count";
- static final String ATTEMPT_LATENCY_NAME = "attempt_latency";
- static final String ATTEMPT_COUNT_NAME = "attempt_count";
- static final String FIRST_RESPONSE_LATENCY_NAME = "first_response_latency";
- static final String END_TO_END_LATENCY_NAME = "end_to_end_latency";
- static final String TRANSACTION_LATENCY_NAME = "transaction_latency";
- static final String TRANSACTION_ATTEMPT_COUNT_NAME = "transaction_attempt_count";
-
- static final String MILLISECOND_UNIT = "ms";
-
- static final String ENABLE_METRICS_ENV_VAR = "FIRESTORE_ENABLE_TRACING";
-
- public static final Set COMMON_ATTRIBUTES =
- ImmutableSet.of(
- PROJECT_ID_KEY,
- DATABASE_ID_KEY,
- CLIENT_UID_KEY,
- CLIENT_LIBRARY_KEY,
- LIBRARY_VERSION_KEY,
- STATUS_KEY);
-
- public static final Set BUILTIN_METRICS =
- ImmutableSet.of(
- OPERATION_LATENCY_NAME,
- ATTEMPT_LATENCY_NAME,
- OPERATION_COUNT_NAME,
- ATTEMPT_COUNT_NAME,
- FIRST_RESPONSE_LATENCY_NAME,
- END_TO_END_LATENCY_NAME,
- TRANSACTION_LATENCY_NAME,
- TRANSACTION_ATTEMPT_COUNT_NAME)
- .stream()
- .map(m -> METER_NAME + '/' + m)
- .collect(Collectors.toSet());
private final FirestoreOptions firestoreOptions;
private OpenTelemetry openTelemetry;
@@ -149,11 +83,15 @@ public MetricsUtil(FirestoreOptions firestoreOptions) {
}
if (createEnabledInstance) {
- createMetricsUtil();
+ try {
+ createMetricsUtil();
+ } catch (IOException e) {
+ System.out.println(e);
+ }
}
}
- private void createMetricsUtil() {
+ private void createMetricsUtil() throws IOException {
this.openTelemetry = getDefaultOpenTelemetryInstance();
OpenTelemetryMetricsRecorder recorder =
@@ -194,7 +132,7 @@ public void firstResponseLatencyRecorder(double latency, Map att
}
}
- private OpenTelemetry getDefaultOpenTelemetryInstance() {
+ private OpenTelemetry getDefaultOpenTelemetryInstance() throws IOException {
OpenTelemetry openTelemetry = firestoreOptions.getOpenTelemetryOptions().getOpenTelemetry();
// If metrics is enabled, but an OpenTelemetry instance is not provided, create a default OTel
@@ -202,28 +140,8 @@ private OpenTelemetry getDefaultOpenTelemetryInstance() {
if (openTelemetry == null) {
SdkMeterProviderBuilder sdkMeterProviderBuilder = SdkMeterProvider.builder();
- // Attach built-in exporter
- MetricExporter metricExporter =
- GoogleCloudMetricExporter.createWithConfiguration(
- MetricConfiguration.builder().setProjectId(firestoreOptions.getProjectId()).build());
- sdkMeterProviderBuilder.registerMetricReader(
- PeriodicMetricReader.builder(metricExporter).build());
-
- // Add static attributes to resource
- Package pkg = this.getClass().getPackage();
- sdkMeterProviderBuilder.setResource(
- Resource.getDefault()
- .merge(
- Resource.builder()
- .put(DATABASE_ID_KEY, firestoreOptions.getDatabaseId())
- .put(CLIENT_LIBRARY_KEY, FIRESTORE_METER_NAME)
- .put(LIBRARY_VERSION_KEY, pkg.getImplementationVersion())
- .build()));
-
- // Filter out attributes that are not defined
- for (Map.Entry entry : getAllViews().entrySet()) {
- sdkMeterProviderBuilder.registerView(entry.getKey(), entry.getValue());
- }
+ BuiltinMetricsView.registerBuiltinMetrics(
+ firestoreOptions.getProjectId(), sdkMeterProviderBuilder);
openTelemetry =
OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProviderBuilder.build()).build();
@@ -283,71 +201,4 @@ public static String extractErrorStatus(@Nullable Throwable throwable) {
return status.getCode().name();
}
-
- static void defineView(
- ImmutableMap.Builder viewMap,
- String id,
- Set attributes) {
- InstrumentSelector selector =
- InstrumentSelector.builder().setName(METER_NAME + "/" + id).build();
- Set attributesFilter =
- ImmutableSet.builder()
- .addAll(attributes.stream().map(AttributeKey::getKey).collect(Collectors.toSet()))
- .build();
- View view = View.builder().setAttributeFilter(attributesFilter).build();
-
- viewMap.put(selector, view);
- }
-
- public static Map getAllViews() {
- ImmutableMap.Builder views = ImmutableMap.builder();
-
- defineView(
- views,
- OPERATION_LATENCY_NAME,
- ImmutableSet.builder()
- .addAll(COMMON_ATTRIBUTES)
- .add(METHOD_NAME_KEY)
- .build());
- defineView(
- views,
- ATTEMPT_LATENCY_NAME,
- ImmutableSet.builder()
- .addAll(COMMON_ATTRIBUTES)
- .add(METHOD_NAME_KEY)
- .build());
-
- defineView(
- views,
- OPERATION_COUNT_NAME,
- ImmutableSet.builder()
- .addAll(COMMON_ATTRIBUTES)
- .add(METHOD_NAME_KEY)
- .build());
-
- defineView(
- views,
- ATTEMPT_COUNT_NAME,
- ImmutableSet.builder()
- .addAll(COMMON_ATTRIBUTES)
- .add(METHOD_NAME_KEY)
- .build());
-
- defineView(
- views,
- FIRST_RESPONSE_LATENCY_NAME,
- ImmutableSet.builder()
- .addAll(COMMON_ATTRIBUTES)
- .add(METHOD_NAME_KEY)
- .build());
-
- defineView(
- views,
- END_TO_END_LATENCY_NAME,
- ImmutableSet.builder()
- .addAll(COMMON_ATTRIBUTES)
- .add(METHOD_NAME_KEY)
- .build());
- return views.build();
- }
}
From da0fe82ef5387fe7ee2daf36f632558a966c4a08 Mon Sep 17 00:00:00 2001
From: milaGGL <107142260+milaGGL@users.noreply.github.com>
Date: Fri, 23 Aug 2024 16:06:23 -0400
Subject: [PATCH 006/103] format
---
.../telemetry/BuiltinMetricsConstants.java | 16 ++++++++++++++++
.../firestore/telemetry/BuiltinMetricsView.java | 3 +--
2 files changed, 17 insertions(+), 2 deletions(-)
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java
index 181802dc1..17a210796 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java
@@ -1,3 +1,19 @@
+/*
+ * Copyright 2024 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.firestore.telemetry;
import com.google.api.gax.tracing.OpenTelemetryMetricsRecorder;
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java
index db9ebb6e4..c42f131ef 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java
@@ -1,5 +1,3 @@
-package com.google.cloud.firestore.telemetry;
-
/*
* Copyright 2023 Google LLC
*
@@ -15,6 +13,7 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+package com.google.cloud.firestore.telemetry;
import static com.google.cloud.firestore.telemetry.BuiltinMetricsConstants.*;
From 61432eb6efd31720f18a0283e75f23485fec6bdc Mon Sep 17 00:00:00 2001
From: milaGGL <107142260+milaGGL@users.noreply.github.com>
Date: Mon, 26 Aug 2024 15:52:27 -0400
Subject: [PATCH 007/103] add additional attributes
---
.../cloud/firestore/spi/v1/GrpcFirestoreRpc.java | 3 ++-
.../cloud/firestore/telemetry/MetricsUtil.java | 13 ++++++++++---
2 files changed, 12 insertions(+), 4 deletions(-)
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1/GrpcFirestoreRpc.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1/GrpcFirestoreRpc.java
index 9ddc4998b..36cd8dac2 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1/GrpcFirestoreRpc.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/spi/v1/GrpcFirestoreRpc.java
@@ -160,7 +160,8 @@ public GrpcFirestoreRpc(final FirestoreOptions options) throws IOException {
if (options.getApiTracerFactory() != null) {
firestoreBuilder.setTracerFactory(options.getApiTracerFactory());
} else if (options.getOpenTelemetryOptions().isMetricsEnabled()) {
- // TODO: move this logic to Firestore options
+ // TODO: it should not be determined by isMetricsEnabled. move this whole logic to
+ // options.getApiTracerFactory()
firestoreBuilder.setTracerFactory(
options.getMetricsUtil().getOpenTelemetryApiTracerFactory());
}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java
index e346d7c1a..62e159fdf 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java
@@ -39,6 +39,7 @@
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder;
import java.io.IOException;
+import java.util.HashMap;
import java.util.Map;
import javax.annotation.Nullable;
@@ -84,20 +85,26 @@ public MetricsUtil(FirestoreOptions firestoreOptions) {
if (createEnabledInstance) {
try {
- createMetricsUtil();
+ createMetricsUtil(firestoreOptions);
} catch (IOException e) {
System.out.println(e);
}
}
}
- private void createMetricsUtil() throws IOException {
+ private void createMetricsUtil(FirestoreOptions firestoreOptions) throws IOException {
this.openTelemetry = getDefaultOpenTelemetryInstance();
OpenTelemetryMetricsRecorder recorder =
new OpenTelemetryMetricsRecorder(openTelemetry, METER_NAME);
- this.otelApiTracerFactory = new MetricsTracerFactory(recorder);
+ Map attributes = new HashMap<>();
+ attributes.put(DATABASE_ID_KEY.toString(), firestoreOptions.getDatabaseId());
+ attributes.put(PROJECT_ID_KEY.toString(), firestoreOptions.getProjectId());
+
+ System.out.println("====1");
+ this.otelApiTracerFactory = new MetricsTracerFactory(recorder, attributes);
+ System.out.println("====2");
registerMetrics();
}
From f0a029d9e73b7225a7008ed59b7b16c17c256dea Mon Sep 17 00:00:00 2001
From: milaGGL <107142260+milaGGL@users.noreply.github.com>
Date: Tue, 27 Aug 2024 13:45:33 -0400
Subject: [PATCH 008/103] add attributes to gax metrics
---
.../com/google/cloud/firestore/FirestoreOptions.java | 4 +++-
.../firestore/telemetry/BuiltinMetricsConstants.java | 2 +-
.../cloud/firestore/telemetry/BuiltinMetricsView.java | 3 ---
.../google/cloud/firestore/telemetry/MetricsUtil.java | 11 ++++++-----
4 files changed, 10 insertions(+), 10 deletions(-)
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java
index 259082a3f..512b71364 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreOptions.java
@@ -326,13 +326,15 @@ protected FirestoreOptions(Builder builder) {
? builder.openTelemetryOptions
: FirestoreOpenTelemetryOptions.newBuilder().build();
this.traceUtil = com.google.cloud.firestore.telemetry.TraceUtil.getInstance(this);
- this.metricsUtil = new MetricsUtil(this);
this.databaseId =
builder.databaseId != null
? builder.databaseId
: FirestoreDefaults.INSTANCE.getDatabaseId();
+ // set metrics util after database ID is set
+ this.metricsUtil = new MetricsUtil(this);
+
if (builder.channelProvider == null) {
ApiFunction channelConfigurator =
this.traceUtil.getChannelConfigurator();
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java
index 17a210796..093111d06 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java
@@ -28,7 +28,7 @@
public class BuiltinMetricsConstants {
- static final String FIRESTORE_METER_NAME = "firestore_java";
+ static final String FIRESTORE_LIBRARY_NAME = "firestore_java";
// TODO: change to firestore.googleapis.com
public static final String METER_NAME = "custom.googleapis.com/internal/client";
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java
index c42f131ef..0871471f4 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java
@@ -42,9 +42,6 @@ public static void registerBuiltinMetrics(String projectId, SdkMeterProviderBuil
builder.registerMetricReader(PeriodicMetricReader.builder(metricExporter).build());
- // Add static attributes to resource
- // Package pkg = this.getClass().getPackage();
-
// Filter out attributes that are not defined
for (Map.Entry entry : getAllViews().entrySet()) {
builder.registerView(entry.getKey(), entry.getValue());
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java
index 62e159fdf..a4e851c88 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java
@@ -100,17 +100,18 @@ private void createMetricsUtil(FirestoreOptions firestoreOptions) throws IOExcep
Map attributes = new HashMap<>();
attributes.put(DATABASE_ID_KEY.toString(), firestoreOptions.getDatabaseId());
- attributes.put(PROJECT_ID_KEY.toString(), firestoreOptions.getProjectId());
+ attributes.put(CLIENT_LIBRARY_KEY.toString(), FIRESTORE_LIBRARY_NAME);
+ String pkgVersion = this.getClass().getPackage().getImplementationVersion();
+ if (pkgVersion != null) {
+ attributes.put(LIBRARY_VERSION_KEY.toString(), pkgVersion);
+ }
- System.out.println("====1");
this.otelApiTracerFactory = new MetricsTracerFactory(recorder, attributes);
- System.out.println("====2");
-
registerMetrics();
}
void registerMetrics() {
- this.meter = openTelemetry.getMeter(FIRESTORE_METER_NAME);
+ this.meter = openTelemetry.getMeter(FIRESTORE_LIBRARY_NAME);
this.endToEndRequestLatency =
meter
From 54560387caf541e2ca08cfeaf4279510683b6647 Mon Sep 17 00:00:00 2001
From: milaGGL <107142260+milaGGL@users.noreply.github.com>
Date: Wed, 4 Sep 2024 11:41:14 -0400
Subject: [PATCH 009/103] Update BuiltinMetricsConstants.java
---
.../telemetry/BuiltinMetricsConstants.java | 57 +++++++++----------
1 file changed, 28 insertions(+), 29 deletions(-)
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java
index 093111d06..f436cf4fe 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsConstants.java
@@ -32,6 +32,7 @@ public class BuiltinMetricsConstants {
// TODO: change to firestore.googleapis.com
public static final String METER_NAME = "custom.googleapis.com/internal/client";
+ static final String FIRESTORE_METER_NAME = "firestore_java";
public static final String GAX_METER_NAME = OpenTelemetryMetricsRecorder.GAX_METER_NAME;
// Metric attribute keys for monitored resource
@@ -89,9 +90,10 @@ public class BuiltinMetricsConstants {
static void defineView(
ImmutableMap.Builder viewMap,
String id,
+ String meter,
Set attributes) {
InstrumentSelector selector =
- InstrumentSelector.builder().setName(METER_NAME + "/" + id).build();
+ InstrumentSelector.builder().setMeterName(meter).setName(METER_NAME + "/" + id).build();
Set attributesFilter =
ImmutableSet.builder()
.addAll(attributes.stream().map(AttributeKey::getKey).collect(Collectors.toSet()))
@@ -101,55 +103,52 @@ static void defineView(
viewMap.put(selector, view);
}
+ private static Set withAdditionalAttributes(Set attributes) {
+ return ImmutableSet.builder()
+ .addAll(COMMON_ATTRIBUTES)
+ .addAll(attributes)
+ .build();
+ }
+
public static Map getAllViews() {
ImmutableMap.Builder views = ImmutableMap.builder();
+ // Define views with COMMON_ATTRIBUTES and METHOD_NAME_KEY
defineView(
views,
OPERATION_LATENCY_NAME,
- ImmutableSet.builder()
- .addAll(COMMON_ATTRIBUTES)
- .add(METHOD_NAME_KEY)
- .build());
+ GAX_METER_NAME,
+ withAdditionalAttributes(ImmutableSet.of(METHOD_NAME_KEY)));
defineView(
views,
ATTEMPT_LATENCY_NAME,
- ImmutableSet.builder()
- .addAll(COMMON_ATTRIBUTES)
- .add(METHOD_NAME_KEY)
- .build());
-
+ GAX_METER_NAME,
+ withAdditionalAttributes(ImmutableSet.of(METHOD_NAME_KEY)));
defineView(
views,
OPERATION_COUNT_NAME,
- ImmutableSet.builder()
- .addAll(COMMON_ATTRIBUTES)
- .add(METHOD_NAME_KEY)
- .build());
-
+ GAX_METER_NAME,
+ withAdditionalAttributes(ImmutableSet.of(METHOD_NAME_KEY)));
defineView(
views,
ATTEMPT_COUNT_NAME,
- ImmutableSet.builder()
- .addAll(COMMON_ATTRIBUTES)
- .add(METHOD_NAME_KEY)
- .build());
-
+ GAX_METER_NAME,
+ withAdditionalAttributes(ImmutableSet.of(METHOD_NAME_KEY)));
defineView(
views,
FIRST_RESPONSE_LATENCY_NAME,
- ImmutableSet.builder()
- .addAll(COMMON_ATTRIBUTES)
- .add(METHOD_NAME_KEY)
- .build());
-
+ FIRESTORE_METER_NAME,
+ withAdditionalAttributes(ImmutableSet.of(METHOD_NAME_KEY)));
defineView(
views,
END_TO_END_LATENCY_NAME,
- ImmutableSet.builder()
- .addAll(COMMON_ATTRIBUTES)
- .add(METHOD_NAME_KEY)
- .build());
+ FIRESTORE_METER_NAME,
+ withAdditionalAttributes(ImmutableSet.of(METHOD_NAME_KEY)));
+
+ // Define views with only COMMON_ATTRIBUTES
+ defineView(views, TRANSACTION_LATENCY_NAME, FIRESTORE_METER_NAME, COMMON_ATTRIBUTES);
+ defineView(views, TRANSACTION_ATTEMPT_COUNT_NAME, FIRESTORE_METER_NAME, COMMON_ATTRIBUTES);
+
return views.build();
}
}
From 5efaba63ed1523d323e5664dfcd6ceacc02eda8d Mon Sep 17 00:00:00 2001
From: milaGGL <107142260+milaGGL@users.noreply.github.com>
Date: Mon, 9 Sep 2024 14:18:53 -0400
Subject: [PATCH 010/103] remove current metrics implementation
---
.../cloud/firestore/DocumentReference.java | 7 ------
.../google/cloud/firestore/FirestoreImpl.java | 24 -------------------
.../com/google/cloud/firestore/Query.java | 17 -------------
.../telemetry/BuiltinMetricsView.java | 5 +++-
4 files changed, 4 insertions(+), 49 deletions(-)
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java
index bbad025e7..9ecad6e95 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java
@@ -21,13 +21,11 @@
import com.google.api.core.InternalExtensionOnly;
import com.google.api.gax.rpc.ApiException;
import com.google.api.gax.rpc.ApiExceptions;
-import com.google.cloud.firestore.telemetry.MetricsUtil;
import com.google.cloud.firestore.telemetry.TraceUtil;
import com.google.cloud.firestore.telemetry.TraceUtil.Scope;
import com.google.cloud.firestore.v1.FirestoreClient.ListCollectionIdsPagedResponse;
import com.google.common.util.concurrent.MoreExecutors;
import com.google.firestore.v1.ListCollectionIdsRequest;
-import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
@@ -476,13 +474,8 @@ public ApiFuture delete() {
public ApiFuture get() {
TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_GET);
- MetricsUtil util = getFirestore().getOptions().getMetricsUtil();
- double start = System.currentTimeMillis();
try (Scope ignored = span.makeCurrent()) {
ApiFuture result = extractFirst(rpcContext.getFirestore().getAll(this));
- Map attributes = new HashMap<>();
- attributes.put("method_name", "DocumentReference.get");
- util.endAtFuture(result, start, attributes);
span.endAtFuture(result);
return result;
} catch (Exception error) {
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java
index dcf8a18cc..48c691466 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/FirestoreImpl.java
@@ -32,7 +32,6 @@
import com.google.api.gax.rpc.UnaryCallable;
import com.google.cloud.Timestamp;
import com.google.cloud.firestore.spi.v1.FirestoreRpc;
-import com.google.cloud.firestore.telemetry.MetricsUtil;
import com.google.cloud.firestore.telemetry.TraceUtil;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
@@ -228,12 +227,6 @@ void getAll(
// that we receive from the server.
final int NUM_RESPONSES_PER_TRACE_EVENT = 100;
- MetricsUtil util = getFirestore().getOptions().getMetricsUtil();
- double start = System.currentTimeMillis();
- Map attributes = new HashMap<>();
- String method = transactionId != null ? "Batch.get" : "Transaction.get";
- attributes.put("method_name", method);
-
ResponseObserver responseObserver =
new ResponseObserver() {
int numResponses = 0;
@@ -261,12 +254,6 @@ public void onResponse(BatchGetDocumentsResponse response) {
getTraceUtil()
.currentSpan()
.addEvent(TraceUtil.SPAN_NAME_BATCH_GET_DOCUMENTS + ": First response received");
-
- double end = System.currentTimeMillis();
- double elapsedTime = end - start;
- attributes.put("status", "OK");
- util.firstResponseLatencyRecorder(elapsedTime, attributes);
-
} else if (numResponses % NUM_RESPONSES_PER_TRACE_EVENT == 0) {
getTraceUtil()
.currentSpan()
@@ -275,11 +262,6 @@ public void onResponse(BatchGetDocumentsResponse response) {
+ ": Received "
+ numResponses
+ " responses");
-
- double end = System.currentTimeMillis();
- double elapsedTime = end - start;
- attributes.put("status", "OK");
- util.endToEndRequestLatencyRecorder(elapsedTime, attributes);
}
switch (response.getResultCase()) {
@@ -316,12 +298,6 @@ public void onResponse(BatchGetDocumentsResponse response) {
@Override
public void onError(Throwable throwable) {
getTraceUtil().currentSpan().end(throwable);
-
- double end = System.currentTimeMillis();
- double elapsedTime = end - start;
- attributes.put("status", MetricsUtil.extractErrorStatus(throwable));
- util.endToEndRequestLatencyRecorder(elapsedTime, attributes);
-
apiStreamObserver.onError(throwable);
}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java
index 58431de14..b19fe8276 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java
@@ -39,7 +39,6 @@
import com.google.auto.value.AutoValue;
import com.google.cloud.Timestamp;
import com.google.cloud.firestore.Query.QueryOptions.Builder;
-import com.google.cloud.firestore.telemetry.MetricsUtil;
import com.google.cloud.firestore.telemetry.TraceUtil;
import com.google.cloud.firestore.telemetry.TraceUtil.Scope;
import com.google.cloud.firestore.v1.FirestoreSettings;
@@ -66,11 +65,9 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
-import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.SortedSet;
@@ -1742,13 +1739,6 @@ private void internalStream(
.build());
final AtomicReference lastReceivedDocument = new AtomicReference<>();
-
- MetricsUtil util = getFirestore().getOptions().getMetricsUtil();
- long start = System.currentTimeMillis();
- Map attributes = new HashMap<>();
- String method = transactionId != null ? "Query.get" : "Transaction.get";
- attributes.put("method_name", method);
-
ResponseObserver observer =
new ResponseObserver() {
Timestamp readTime;
@@ -1977,11 +1967,6 @@ ApiFuture get(
? TraceUtil.SPAN_NAME_QUERY_GET
: TraceUtil.SPAN_NAME_TRANSACTION_GET_QUERY);
- MetricsUtil util = getFirestore().getOptions().getMetricsUtil();
- long start = System.currentTimeMillis();
- Map attributes = new HashMap<>();
- String method = transactionId != null ? "Query.get" : "Transaction.get";
- attributes.put("method_name", method);
try (Scope ignored = span.makeCurrent()) {
final SettableApiFuture result = SettableApiFuture.create();
internalStream(
@@ -2028,8 +2013,6 @@ public void onCompleted() {
/* isRetryRequestWithCursor= */ false);
span.endAtFuture(result);
- util.endAtFuture(result, start, attributes);
-
return result;
} catch (Exception error) {
span.end(error);
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java
index 0871471f4..1a2941b8d 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsView.java
@@ -38,7 +38,10 @@ public static void registerBuiltinMetrics(String projectId, SdkMeterProviderBuil
// Attach built-in exporter
MetricExporter metricExporter =
GoogleCloudMetricExporter.createWithConfiguration(
- MetricConfiguration.builder().setProjectId(projectId).build());
+ MetricConfiguration.builder()
+ .setProjectId(projectId)
+ .setInstrumentationLibraryLabelsEnabled(false)
+ .build());
builder.registerMetricReader(PeriodicMetricReader.builder(metricExporter).build());
From c0b4cc17ed001432ec98ed85835de72f5946d1ea Mon Sep 17 00:00:00 2001
From: milaGGL <107142260+milaGGL@users.noreply.github.com>
Date: Tue, 10 Sep 2024 20:38:23 -0400
Subject: [PATCH 011/103] add custom exporter, add latency recorder
---
.../cloud/firestore/AggregateQuery.java | 25 +++
.../google/cloud/firestore/BulkWriter.java | 11 ++
.../cloud/firestore/CollectionGroup.java | 15 ++
.../cloud/firestore/CollectionReference.java | 26 +++
.../cloud/firestore/DocumentReference.java | 108 +++++++++++
.../com/google/cloud/firestore/Query.java | 33 ++++
.../cloud/firestore/ReadTimeTransaction.java | 31 +++
.../firestore/ServerSideTransaction.java | 41 ++++
.../ServerSideTransactionRunner.java | 11 ++
.../google/cloud/firestore/Transaction.java | 6 +
.../google/cloud/firestore/UpdateBuilder.java | 14 ++
.../telemetry/BuiltinMetricsConstants.java | 16 +-
.../telemetry/BuiltinMetricsView.java | 13 +-
.../FirestoreCloudMonitoringExporter.java | 91 +++++++++
.../firestore/telemetry/MetricsUtil.java | 178 +++++++++++-------
15 files changed, 531 insertions(+), 88 deletions(-)
create mode 100644 google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/FirestoreCloudMonitoringExporter.java
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java
index 84b4a0478..72fbeda08 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/AggregateQuery.java
@@ -27,6 +27,8 @@
import com.google.api.gax.rpc.StatusCode;
import com.google.api.gax.rpc.StreamController;
import com.google.cloud.Timestamp;
+import com.google.cloud.firestore.telemetry.MetricsUtil;
+import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext;
import com.google.cloud.firestore.telemetry.TraceUtil;
import com.google.cloud.firestore.telemetry.TraceUtil.Scope;
import com.google.cloud.firestore.v1.FirestoreSettings;
@@ -70,6 +72,11 @@ private TraceUtil getTraceUtil() {
return query.getFirestore().getOptions().getTraceUtil();
}
+ @Nonnull
+ private MetricsUtil getMetricsUtil() {
+ return query.getFirestore().getOptions().getMetricsUtil();
+ }
+
/** Returns the query whose aggregations will be calculated by this object. */
@Nonnull
public Query getQuery() {
@@ -97,6 +104,10 @@ public ApiFuture get() {
@Nonnull
public ApiFuture> explain(ExplainOptions options) {
TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_AGGREGATION_QUERY_GET);
+
+ MetricsContext metricsContext =
+ getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_AGGREGATION_QUERY_GET);
+
try (Scope ignored = span.makeCurrent()) {
AggregateQueryExplainResponseDeliverer responseDeliverer =
new AggregateQueryExplainResponseDeliverer(
@@ -106,10 +117,14 @@ public ApiFuture> explain(ExplainOptions
/* explainOptions= */ options);
runQuery(responseDeliverer, /* attempt */ 0);
ApiFuture> result = responseDeliverer.getFuture();
+
span.endAtFuture(result);
+ metricsContext.recordEndToEndLatencyAtFuture(result);
+
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordEndToEndLatency(error);
throw error;
}
}
@@ -123,6 +138,14 @@ ApiFuture get(
transactionId == null
? TraceUtil.SPAN_NAME_AGGREGATION_QUERY_GET
: TraceUtil.SPAN_NAME_TRANSACTION_GET_AGGREGATION_QUERY);
+
+ MetricsContext metricsContext =
+ getMetricsUtil()
+ .createMetricsContext(
+ transactionId == null
+ ? TraceUtil.SPAN_NAME_AGGREGATION_QUERY_GET
+ : TraceUtil.SPAN_NAME_TRANSACTION_GET_AGGREGATION_QUERY);
+
try (Scope ignored = span.makeCurrent()) {
AggregateQueryResponseDeliverer responseDeliverer =
new AggregateQueryResponseDeliverer(
@@ -132,9 +155,11 @@ ApiFuture get(
runQuery(responseDeliverer, /* attempt= */ 0);
ApiFuture result = responseDeliverer.getFuture();
span.endAtFuture(result);
+ metricsContext.recordEndToEndLatencyAtFuture(result);
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordEndToEndLatency(error);
throw error;
}
}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/BulkWriter.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/BulkWriter.java
index bc881979c..f12f42cc5 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/BulkWriter.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/BulkWriter.java
@@ -26,6 +26,7 @@
import com.google.api.core.SettableApiFuture;
import com.google.api.gax.rpc.ApiException;
import com.google.api.gax.rpc.StatusCode.Code;
+import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext;
import com.google.cloud.firestore.telemetry.TraceUtil;
import com.google.cloud.firestore.telemetry.TraceUtil.Context;
import com.google.cloud.firestore.telemetry.TraceUtil.Scope;
@@ -914,6 +915,13 @@ private void sendBatchLocked(final BulkCommitBatch batch, final boolean flush) {
.getTraceUtil()
.startSpan(TraceUtil.SPAN_NAME_BULK_WRITER_COMMIT, traceContext)
.setAttribute(ATTRIBUTE_KEY_DOC_COUNT, batch.getMutationsSize());
+
+ MetricsContext metricsContext =
+ firestore
+ .getOptions()
+ .getMetricsUtil()
+ .createMetricsContext(TraceUtil.SPAN_NAME_BULK_WRITER_COMMIT);
+
try (Scope ignored = span.makeCurrent()) {
ApiFuture result = batch.bulkCommit();
result.addListener(
@@ -926,8 +934,11 @@ private void sendBatchLocked(final BulkCommitBatch batch, final boolean flush) {
},
bulkWriterExecutor);
span.endAtFuture(result);
+ metricsContext.recordEndToEndLatencyAtFuture(result);
+
} catch (Exception error) {
span.end(error);
+ metricsContext.recordEndToEndLatency(error);
throw error;
}
} else {
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionGroup.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionGroup.java
index 3026e5183..d408194d5 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionGroup.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionGroup.java
@@ -21,6 +21,7 @@
import com.google.api.gax.rpc.ApiException;
import com.google.api.gax.rpc.ApiExceptions;
import com.google.api.gax.rpc.ApiStreamObserver;
+import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext;
import com.google.cloud.firestore.telemetry.TraceUtil;
import com.google.cloud.firestore.telemetry.TraceUtil.Scope;
import com.google.cloud.firestore.v1.FirestoreClient.PartitionQueryPagedResponse;
@@ -110,6 +111,14 @@ public ApiFuture> getPartitions(long desiredPartitionCount)
.getOptions()
.getTraceUtil()
.startSpan(TraceUtil.SPAN_NAME_PARTITION_QUERY);
+
+ MetricsContext metricsContext =
+ rpcContext
+ .getFirestore()
+ .getOptions()
+ .getMetricsUtil()
+ .createMetricsContext(TraceUtil.SPAN_NAME_PARTITION_QUERY);
+
try (Scope ignored = span.makeCurrent()) {
ApiFuture> result =
ApiFutures.transform(
@@ -127,12 +136,18 @@ public ApiFuture> getPartitions(long desiredPartitionCount)
},
MoreExecutors.directExecutor());
span.endAtFuture(result);
+ metricsContext.recordEndToEndLatencyAtFuture(result);
+
return result;
} catch (ApiException exception) {
span.end(exception);
+ metricsContext.recordEndToEndLatency(exception);
+
throw FirestoreException.forApiException(exception);
} catch (Throwable throwable) {
span.end(throwable);
+ metricsContext.recordEndToEndLatency(throwable);
+
throw throwable;
}
}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java
index c736d7028..ed84992ab 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/CollectionReference.java
@@ -23,6 +23,7 @@
import com.google.api.gax.rpc.ApiExceptions;
import com.google.api.gax.rpc.UnaryCallable;
import com.google.cloud.firestore.spi.v1.FirestoreRpc;
+import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext;
import com.google.cloud.firestore.telemetry.TraceUtil;
import com.google.cloud.firestore.telemetry.TraceUtil.Scope;
import com.google.cloud.firestore.v1.FirestoreClient.ListDocumentsPagedResponse;
@@ -134,6 +135,14 @@ public Iterable listDocuments() {
.getOptions()
.getTraceUtil()
.startSpan(TraceUtil.SPAN_NAME_COL_REF_LIST_DOCUMENTS);
+
+ MetricsContext metricsContext =
+ rpcContext
+ .getFirestore()
+ .getOptions()
+ .getMetricsUtil()
+ .createMetricsContext(TraceUtil.SPAN_NAME_COL_REF_LIST_DOCUMENTS);
+
try (Scope ignored = span.makeCurrent()) {
ListDocumentsRequest.Builder request = ListDocumentsRequest.newBuilder();
request.setParent(options.getParentPath().toString());
@@ -173,12 +182,18 @@ public void remove() {
}
};
span.end();
+ metricsContext.recordEndToEndLatency();
+
return result;
} catch (ApiException exception) {
span.end(exception);
+ metricsContext.recordEndToEndLatency(exception);
+
throw FirestoreException.forApiException(exception);
} catch (Throwable throwable) {
span.end(throwable);
+ metricsContext.recordEndToEndLatency(throwable);
+
throw throwable;
}
}
@@ -200,6 +215,14 @@ public ApiFuture add(@Nonnull final Map field
.getOptions()
.getTraceUtil()
.startSpan(TraceUtil.SPAN_NAME_COL_REF_ADD);
+
+ MetricsContext metricsContext =
+ rpcContext
+ .getFirestore()
+ .getOptions()
+ .getMetricsUtil()
+ .createMetricsContext(TraceUtil.SPAN_NAME_COL_REF_ADD);
+
try (Scope ignored = span.makeCurrent()) {
final DocumentReference documentReference = document();
ApiFuture createFuture = documentReference.create(fields);
@@ -207,9 +230,12 @@ public ApiFuture add(@Nonnull final Map field
ApiFutures.transform(
createFuture, writeResult -> documentReference, MoreExecutors.directExecutor());
span.endAtFuture(result);
+ metricsContext.recordEndToEndLatencyAtFuture(result);
+
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordEndToEndLatency(error);
throw error;
}
}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java
index 9ecad6e95..abc68409d 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/DocumentReference.java
@@ -21,6 +21,8 @@
import com.google.api.core.InternalExtensionOnly;
import com.google.api.gax.rpc.ApiException;
import com.google.api.gax.rpc.ApiExceptions;
+import com.google.cloud.firestore.telemetry.MetricsUtil;
+import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext;
import com.google.cloud.firestore.telemetry.TraceUtil;
import com.google.cloud.firestore.telemetry.TraceUtil.Scope;
import com.google.cloud.firestore.v1.FirestoreClient.ListCollectionIdsPagedResponse;
@@ -139,6 +141,11 @@ private TraceUtil getTraceUtil() {
return getFirestore().getOptions().getTraceUtil();
}
+ @Nonnull
+ private MetricsUtil getMetricsUtil() {
+ return getFirestore().getOptions().getMetricsUtil();
+ }
+
/**
* Creates a new Document at the DocumentReference's Location. It fails the write if the document
* exists.
@@ -149,13 +156,20 @@ private TraceUtil getTraceUtil() {
@Nonnull
public ApiFuture create(@Nonnull Map fields) {
TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_CREATE);
+
+ MetricsContext metricsContext =
+ getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_CREATE);
+
try (Scope ignored = span.makeCurrent()) {
WriteBatch writeBatch = rpcContext.getFirestore().batch();
ApiFuture result = extractFirst(writeBatch.create(this, fields).commit());
span.endAtFuture(result);
+ metricsContext.recordEndToEndLatencyAtFuture(result);
+
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordEndToEndLatency(error);
throw error;
}
}
@@ -170,13 +184,19 @@ public ApiFuture create(@Nonnull Map fields) {
@Nonnull
public ApiFuture create(@Nonnull Object pojo) {
TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_CREATE);
+ MetricsContext metricsContext =
+ getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_CREATE);
+
try (Scope ignored = span.makeCurrent()) {
WriteBatch writeBatch = rpcContext.getFirestore().batch();
ApiFuture result = extractFirst(writeBatch.create(this, pojo).commit());
span.endAtFuture(result);
+ metricsContext.recordEndToEndLatencyAtFuture(result);
+
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordEndToEndLatency(error);
throw error;
}
}
@@ -191,13 +211,19 @@ public ApiFuture create(@Nonnull Object pojo) {
@Nonnull
public ApiFuture set(@Nonnull Map fields) {
TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_SET);
+ MetricsContext metricsContext =
+ getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_SET);
+
try (Scope ignored = span.makeCurrent()) {
WriteBatch writeBatch = rpcContext.getFirestore().batch();
ApiFuture result = extractFirst(writeBatch.set(this, fields).commit());
span.endAtFuture(result);
+ metricsContext.recordEndToEndLatencyAtFuture(result);
+
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordEndToEndLatency(error);
throw error;
}
}
@@ -215,13 +241,19 @@ public ApiFuture set(@Nonnull Map fields) {
public ApiFuture set(
@Nonnull Map fields, @Nonnull SetOptions options) {
TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_SET);
+ MetricsContext metricsContext =
+ getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_SET);
+
try (Scope ignored = span.makeCurrent()) {
WriteBatch writeBatch = rpcContext.getFirestore().batch();
ApiFuture result = extractFirst(writeBatch.set(this, fields, options).commit());
span.endAtFuture(result);
+ metricsContext.recordEndToEndLatencyAtFuture(result);
+
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordEndToEndLatency(error);
throw error;
}
}
@@ -236,13 +268,19 @@ public ApiFuture set(
@Nonnull
public ApiFuture set(@Nonnull Object pojo) {
TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_SET);
+ MetricsContext metricsContext =
+ getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_SET);
+
try (Scope ignored = span.makeCurrent()) {
WriteBatch writeBatch = rpcContext.getFirestore().batch();
ApiFuture result = extractFirst(writeBatch.set(this, pojo).commit());
span.endAtFuture(result);
+ metricsContext.recordEndToEndLatencyAtFuture(result);
+
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordEndToEndLatency(error);
throw error;
}
}
@@ -259,13 +297,19 @@ public ApiFuture set(@Nonnull Object pojo) {
@Nonnull
public ApiFuture set(@Nonnull Object pojo, @Nonnull SetOptions options) {
TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_SET);
+ MetricsContext metricsContext =
+ getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_SET);
+
try (Scope ignored = span.makeCurrent()) {
WriteBatch writeBatch = rpcContext.getFirestore().batch();
ApiFuture result = extractFirst(writeBatch.set(this, pojo, options).commit());
span.endAtFuture(result);
+ metricsContext.recordEndToEndLatencyAtFuture(result);
+
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordEndToEndLatency(error);
throw error;
}
}
@@ -280,13 +324,19 @@ public ApiFuture set(@Nonnull Object pojo, @Nonnull SetOptions opti
@Nonnull
public ApiFuture update(@Nonnull Map fields) {
TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_UPDATE);
+ MetricsContext metricsContext =
+ getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_UPDATE);
+
try (Scope ignored = span.makeCurrent()) {
WriteBatch writeBatch = rpcContext.getFirestore().batch();
ApiFuture result = extractFirst(writeBatch.update(this, fields).commit());
span.endAtFuture(result);
+ metricsContext.recordEndToEndLatencyAtFuture(result);
+
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordEndToEndLatency(error);
throw error;
}
}
@@ -302,14 +352,20 @@ public ApiFuture update(@Nonnull Map fields) {
@Nonnull
public ApiFuture update(@Nonnull Map fields, Precondition options) {
TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_UPDATE);
+ MetricsContext metricsContext =
+ getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_UPDATE);
+
try (Scope ignored = span.makeCurrent()) {
WriteBatch writeBatch = rpcContext.getFirestore().batch();
ApiFuture result =
extractFirst(writeBatch.update(this, fields, options).commit());
span.endAtFuture(result);
+ metricsContext.recordEndToEndLatencyAtFuture(result);
+
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordEndToEndLatency(error);
throw error;
}
}
@@ -327,14 +383,20 @@ public ApiFuture update(@Nonnull Map fields, Precon
public ApiFuture update(
@Nonnull String field, @Nullable Object value, Object... moreFieldsAndValues) {
TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_UPDATE);
+ MetricsContext metricsContext =
+ getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_UPDATE);
+
try (Scope ignored = span.makeCurrent()) {
WriteBatch writeBatch = rpcContext.getFirestore().batch();
ApiFuture result =
extractFirst(writeBatch.update(this, field, value, moreFieldsAndValues).commit());
span.endAtFuture(result);
+ metricsContext.recordEndToEndLatencyAtFuture(result);
+
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordEndToEndLatency(error);
throw error;
}
}
@@ -352,14 +414,20 @@ public ApiFuture update(
public ApiFuture update(
@Nonnull FieldPath fieldPath, @Nullable Object value, Object... moreFieldsAndValues) {
TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_UPDATE);
+ MetricsContext metricsContext =
+ getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_UPDATE);
+
try (Scope ignored = span.makeCurrent()) {
WriteBatch writeBatch = rpcContext.getFirestore().batch();
ApiFuture result =
extractFirst(writeBatch.update(this, fieldPath, value, moreFieldsAndValues).commit());
span.endAtFuture(result);
+ metricsContext.recordEndToEndLatencyAtFuture(result);
+
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordEndToEndLatency(error);
throw error;
}
}
@@ -381,15 +449,21 @@ public ApiFuture update(
@Nullable Object value,
Object... moreFieldsAndValues) {
TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_UPDATE);
+ MetricsContext metricsContext =
+ getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_UPDATE);
+
try (Scope ignored = span.makeCurrent()) {
WriteBatch writeBatch = rpcContext.getFirestore().batch();
ApiFuture result =
extractFirst(
writeBatch.update(this, options, field, value, moreFieldsAndValues).commit());
span.endAtFuture(result);
+ metricsContext.recordEndToEndLatencyAtFuture(result);
+
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordEndToEndLatency(error);
throw error;
}
}
@@ -411,15 +485,21 @@ public ApiFuture update(
@Nullable Object value,
Object... moreFieldsAndValues) {
TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_UPDATE);
+ MetricsContext metricsContext =
+ getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_UPDATE);
+
try (Scope ignored = span.makeCurrent()) {
WriteBatch writeBatch = rpcContext.getFirestore().batch();
ApiFuture result =
extractFirst(
writeBatch.update(this, options, fieldPath, value, moreFieldsAndValues).commit());
span.endAtFuture(result);
+ metricsContext.recordEndToEndLatencyAtFuture(result);
+
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordEndToEndLatency(error);
throw error;
}
}
@@ -433,13 +513,19 @@ public ApiFuture update(
@Nonnull
public ApiFuture delete(@Nonnull Precondition options) {
TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_DELETE);
+ MetricsContext metricsContext =
+ getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_DELETE);
+
try (Scope ignored = span.makeCurrent()) {
WriteBatch writeBatch = rpcContext.getFirestore().batch();
ApiFuture result = extractFirst(writeBatch.delete(this, options).commit());
span.endAtFuture(result);
+ metricsContext.recordEndToEndLatencyAtFuture(result);
+
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordEndToEndLatency(error);
throw error;
}
}
@@ -452,13 +538,19 @@ public ApiFuture delete(@Nonnull Precondition options) {
@Nonnull
public ApiFuture delete() {
TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_DELETE);
+ MetricsContext metricsContext =
+ getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_DELETE);
+
try (Scope ignored = span.makeCurrent()) {
WriteBatch writeBatch = rpcContext.getFirestore().batch();
ApiFuture result = extractFirst(writeBatch.delete(this).commit());
span.endAtFuture(result);
+ metricsContext.recordEndToEndLatencyAtFuture(result);
+
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordEndToEndLatency(error);
throw error;
}
}
@@ -473,13 +565,18 @@ public ApiFuture delete() {
@Nonnull
public ApiFuture get() {
TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_GET);
+ MetricsContext metricsContext =
+ getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_GET);
try (Scope ignored = span.makeCurrent()) {
ApiFuture result = extractFirst(rpcContext.getFirestore().getAll(this));
span.endAtFuture(result);
+ metricsContext.recordEndToEndLatencyAtFuture(result);
+
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordEndToEndLatency(error);
throw error;
}
}
@@ -495,13 +592,19 @@ public ApiFuture get() {
@Nonnull
public ApiFuture get(FieldMask fieldMask) {
TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_GET);
+ MetricsContext metricsContext =
+ getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_GET);
+
try (Scope ignored = span.makeCurrent()) {
ApiFuture result =
extractFirst(rpcContext.getFirestore().getAll(new DocumentReference[] {this}, fieldMask));
span.endAtFuture(result);
+ metricsContext.recordEndToEndLatencyAtFuture(result);
+
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordEndToEndLatency(error);
throw error;
}
}
@@ -515,6 +618,9 @@ public ApiFuture get(FieldMask fieldMask) {
@Nonnull
public Iterable listCollections() {
TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_LIST_COLLECTIONS);
+ MetricsContext metricsContext =
+ getMetricsUtil().createMetricsContext(TraceUtil.SPAN_NAME_DOC_REF_LIST_COLLECTIONS);
+
try (Scope ignored = span.makeCurrent()) {
ListCollectionIdsRequest.Builder request = ListCollectionIdsRequest.newBuilder();
request.setParent(path.toString());
@@ -548,9 +654,11 @@ public void remove() {
}
};
span.end();
+ metricsContext.recordEndToEndLatency();
return result;
} catch (ApiException exception) {
span.end(exception);
+ metricsContext.recordEndToEndLatency(exception);
throw FirestoreException.forApiException(exception);
}
}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java
index b19fe8276..593071dcf 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Query.java
@@ -39,6 +39,7 @@
import com.google.auto.value.AutoValue;
import com.google.cloud.Timestamp;
import com.google.cloud.firestore.Query.QueryOptions.Builder;
+import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext;
import com.google.cloud.firestore.telemetry.TraceUtil;
import com.google.cloud.firestore.telemetry.TraceUtil.Scope;
import com.google.cloud.firestore.v1.FirestoreSettings;
@@ -1731,6 +1732,14 @@ private void internalStream(
}
TraceUtil.Span currentSpan = traceUtil.currentSpan();
+
+ // TODO(mila): record transaction latency, retry counts if applicable
+ MetricsContext metricsContext =
+ getFirestore()
+ .getOptions()
+ .getMetricsUtil()
+ .createMetricsContext(TraceUtil.SPAN_NAME_RUN_QUERY);
+
currentSpan.addEvent(
TraceUtil.SPAN_NAME_RUN_QUERY,
new ImmutableMap.Builder()
@@ -1757,6 +1766,7 @@ public void onResponse(RunQueryResponse response) {
if (!firstResponse) {
firstResponse = true;
currentSpan.addEvent(TraceUtil.SPAN_NAME_RUN_QUERY + ": First Response");
+ metricsContext.recordFirstResponseLatency();
}
runQueryResponseObserver.onNext(response);
@@ -1777,6 +1787,7 @@ public void onResponse(RunQueryResponse response) {
if (response.getDone()) {
currentSpan.addEvent(
TraceUtil.SPAN_NAME_RUN_QUERY + ": Received RunQueryResponse.Done");
+ metricsContext.recordEndToEndLatency();
onComplete();
}
}
@@ -1784,6 +1795,7 @@ public void onResponse(RunQueryResponse response) {
@Override
public void onError(Throwable throwable) {
QueryDocumentSnapshot cursor = lastReceivedDocument.get();
+ // TODO(mila): how do we capture e2e latency when retries are present
if (shouldRetry(cursor, throwable)) {
currentSpan.addEvent(
TraceUtil.SPAN_NAME_RUN_QUERY + ": Retryable Error",
@@ -1802,6 +1814,7 @@ public void onError(Throwable throwable) {
currentSpan.addEvent(
TraceUtil.SPAN_NAME_RUN_QUERY + ": Error",
Collections.singletonMap("error.message", throwable.getMessage()));
+ metricsContext.recordEndToEndLatency(throwable);
runQueryResponseObserver.onError(throwable);
}
}
@@ -1861,6 +1874,11 @@ public ApiFuture get() {
public ApiFuture> explain(ExplainOptions options) {
TraceUtil.Span span =
getFirestore().getOptions().getTraceUtil().startSpan(TraceUtil.SPAN_NAME_QUERY_GET);
+ MetricsContext metricsContext =
+ getFirestore()
+ .getOptions()
+ .getMetricsUtil()
+ .createMetricsContext(TraceUtil.SPAN_NAME_QUERY_GET);
try (Scope ignored = span.makeCurrent()) {
final SettableApiFuture> result = SettableApiFuture.create();
@@ -1925,9 +1943,12 @@ public void onCompleted() {
/* isRetryRequestWithCursor= */ false);
span.endAtFuture(result);
+ metricsContext.recordEndToEndLatencyAtFuture(result);
+
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordEndToEndLatency(error);
throw error;
}
}
@@ -1967,6 +1988,15 @@ ApiFuture get(
? TraceUtil.SPAN_NAME_QUERY_GET
: TraceUtil.SPAN_NAME_TRANSACTION_GET_QUERY);
+ MetricsContext metricsContext =
+ getFirestore()
+ .getOptions()
+ .getMetricsUtil()
+ .createMetricsContext(
+ transactionId == null
+ ? TraceUtil.SPAN_NAME_QUERY_GET
+ : TraceUtil.SPAN_NAME_TRANSACTION_GET_QUERY);
+
try (Scope ignored = span.makeCurrent()) {
final SettableApiFuture result = SettableApiFuture.create();
internalStream(
@@ -2013,9 +2043,12 @@ public void onCompleted() {
/* isRetryRequestWithCursor= */ false);
span.endAtFuture(result);
+ metricsContext.recordEndToEndLatencyAtFuture(result);
+
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordEndToEndLatency(error);
throw error;
}
}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ReadTimeTransaction.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ReadTimeTransaction.java
index 0c423469a..4a3daa416 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ReadTimeTransaction.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ReadTimeTransaction.java
@@ -18,6 +18,7 @@
import com.google.api.core.ApiFuture;
import com.google.api.core.ApiFutures;
+import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext;
import com.google.cloud.firestore.telemetry.TraceUtil;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.MoreExecutors;
@@ -58,6 +59,13 @@ public ApiFuture get(@Nonnull DocumentReference documentRef) {
TraceUtil.Span span =
getTraceUtil()
.startSpan(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENT, transactionTraceContext);
+
+ MetricsContext metricsContext =
+ firestore
+ .getOptions()
+ .getMetricsUtil()
+ .createMetricsContext(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENT);
+
try (TraceUtil.Scope ignored = span.makeCurrent()) {
ApiFuture result =
ApiFutures.transform(
@@ -66,9 +74,12 @@ public ApiFuture get(@Nonnull DocumentReference documentRef) {
snapshots -> snapshots.isEmpty() ? null : snapshots.get(0),
MoreExecutors.directExecutor());
span.endAtFuture(result);
+ metricsContext.recordEndToEndLatencyAtFuture(result);
+
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordEndToEndLatency(error);
throw error;
}
}
@@ -80,13 +91,23 @@ public ApiFuture> getAll(
TraceUtil.Span span =
getTraceUtil()
.startSpan(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENTS, transactionTraceContext);
+
+ MetricsContext metricsContext =
+ firestore
+ .getOptions()
+ .getMetricsUtil()
+ .createMetricsContext(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENTS);
+
try (TraceUtil.Scope ignored = span.makeCurrent()) {
ApiFuture> result =
firestore.getAll(documentReferences, /* fieldMask= */ null, readTime);
span.endAtFuture(result);
+ metricsContext.recordEndToEndLatencyAtFuture(result);
+
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordEndToEndLatency(error);
throw error;
}
}
@@ -98,13 +119,23 @@ public ApiFuture> getAll(
TraceUtil.Span span =
getTraceUtil()
.startSpan(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENTS, transactionTraceContext);
+
+ MetricsContext metricsContext =
+ firestore
+ .getOptions()
+ .getMetricsUtil()
+ .createMetricsContext(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENTS);
+
try (TraceUtil.Scope ignored = span.makeCurrent()) {
ApiFuture> result =
firestore.getAll(documentReferences, /* fieldMask= */ null, readTime);
span.endAtFuture(result);
+ metricsContext.recordEndToEndLatencyAtFuture(result);
+
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordEndToEndLatency(error);
throw error;
}
}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransaction.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransaction.java
index 5d366c965..46223ccd1 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransaction.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransaction.java
@@ -19,6 +19,7 @@
import com.google.api.core.ApiFuture;
import com.google.api.core.ApiFutures;
import com.google.cloud.firestore.TransactionOptions.TransactionOptionsType;
+import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext;
import com.google.cloud.firestore.telemetry.TraceUtil;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.MoreExecutors;
@@ -109,6 +110,13 @@ ApiFuture> commit() {
ApiFuture rollback() {
TraceUtil.Span span =
getTraceUtil().startSpan(TraceUtil.SPAN_NAME_TRANSACTION_ROLLBACK, transactionTraceContext);
+
+ MetricsContext metricsContext =
+ firestore
+ .getOptions()
+ .getMetricsUtil()
+ .createMetricsContext(TraceUtil.SPAN_NAME_TRANSACTION_ROLLBACK);
+
try (TraceUtil.Scope ignored = span.makeCurrent()) {
RollbackRequest req =
RollbackRequest.newBuilder()
@@ -135,9 +143,12 @@ ApiFuture rollback() {
},
MoreExecutors.directExecutor());
span.endAtFuture(result);
+ metricsContext.recordEndToEndLatencyAtFuture(result);
+
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordEndToEndLatency(error);
throw error;
}
}
@@ -159,6 +170,13 @@ public ApiFuture get(@Nonnull DocumentReference documentRef) {
TraceUtil.Span span =
getTraceUtil()
.startSpan(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENT, transactionTraceContext);
+
+ MetricsContext metricsContext =
+ firestore
+ .getOptions()
+ .getMetricsUtil()
+ .createMetricsContext(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENT);
+
try (TraceUtil.Scope ignored = span.makeCurrent()) {
Preconditions.checkState(isEmpty(), READ_BEFORE_WRITE_ERROR_MSG);
ApiFuture result =
@@ -171,9 +189,12 @@ public ApiFuture get(@Nonnull DocumentReference documentRef) {
snapshots -> snapshots.isEmpty() ? null : snapshots.get(0),
MoreExecutors.directExecutor());
span.endAtFuture(result);
+ metricsContext.recordEndToEndLatencyAtFuture(result);
+
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordEndToEndLatency(error);
throw error;
}
}
@@ -192,14 +213,24 @@ public ApiFuture