diff --git a/CHANGELOG.md b/CHANGELOG.md
index a3eac07be1..82ee75f8e7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,19 @@
# Changelog
+## [3.29.0](https://github.com/googleapis/java-firestore/compare/v3.28.0...v3.29.0) (2024-11-13)
+
+
+### Bug Fixes
+
+* Drop the setTracingEnabled flag from Options (@BetaApi change) ([#1869](https://github.com/googleapis/java-firestore/issues/1869)) ([712d57e](https://github.com/googleapis/java-firestore/commit/712d57e05da8de41850d94fa19eb253cf3020c0f))
+* Improve error messages in tests ([#1855](https://github.com/googleapis/java-firestore/issues/1855)) ([cb5f9d6](https://github.com/googleapis/java-firestore/commit/cb5f9d6f481c9c839a387579bf60742b7c2c0508))
+* Make StreamableQuery#internalStream package-private to avoid documentation and prevent clirr errors ([593acc3](https://github.com/googleapis/java-firestore/commit/593acc3614f86bdf42387eac5ef8d7ce14dbbec8))
+
+
+### Dependencies
+
+* Update opentelemetry.version to v1.44.1 ([#1924](https://github.com/googleapis/java-firestore/issues/1924)) ([68deeac](https://github.com/googleapis/java-firestore/commit/68deeac02eabb927ba8b91c77f3be8471ba66718))
+
## [3.28.0](https://github.com/googleapis/java-firestore/compare/v3.27.4...v3.28.0) (2024-10-29)
diff --git a/generation_config.yaml b/generation_config.yaml
index b91c21240f..070aa2045d 100644
--- a/generation_config.yaml
+++ b/generation_config.yaml
@@ -1,6 +1,6 @@
gapic_generator_version: 2.49.0
-googleapis_commitish: ba8ea80f25d19bde8501cd51f314391f8d39bde8
-libraries_bom_version: 26.49.0
+googleapis_commitish: 1f2e5aab4f95b9bd38dd1ac8c7486657f93c1975
+libraries_bom_version: 26.50.0
libraries:
- api_shortname: firestore
name_pretty: Cloud Firestore
diff --git a/google-cloud-firestore-admin/pom.xml b/google-cloud-firestore-admin/pom.xml
index 695693f298..fbb61cfec1 100644
--- a/google-cloud-firestore-admin/pom.xml
+++ b/google-cloud-firestore-admin/pom.xml
@@ -4,7 +4,7 @@
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
4.0.0
google-cloud-firestore-admin
- 3.28.0
+ 3.29.0
jar
Google Cloud Firestore Admin Client
https://github.com/googleapis/java-firestore
@@ -14,7 +14,7 @@
com.google.cloud
google-cloud-firestore-parent
- 3.28.0
+ 3.29.0
diff --git a/google-cloud-firestore-bom/pom.xml b/google-cloud-firestore-bom/pom.xml
index 67eade58ba..8aa24a9edf 100644
--- a/google-cloud-firestore-bom/pom.xml
+++ b/google-cloud-firestore-bom/pom.xml
@@ -3,7 +3,7 @@
4.0.0
com.google.cloud
google-cloud-firestore-bom
- 3.28.0
+ 3.29.0
pom
com.google.cloud
@@ -52,37 +52,37 @@
com.google.cloud
google-cloud-firestore
- 3.28.0
+ 3.29.0
com.google.cloud
google-cloud-firestore-admin
- 3.28.0
+ 3.29.0
com.google.api.grpc
grpc-google-cloud-firestore-admin-v1
- 3.28.0
+ 3.29.0
com.google.api.grpc
grpc-google-cloud-firestore-v1
- 3.28.0
+ 3.29.0
com.google.api.grpc
proto-google-cloud-firestore-admin-v1
- 3.28.0
+ 3.29.0
com.google.api.grpc
proto-google-cloud-firestore-v1
- 3.28.0
+ 3.29.0
com.google.cloud
proto-google-cloud-firestore-bundle-v1
- 3.28.0
+ 3.29.0
diff --git a/google-cloud-firestore/clirr-ignored-differences.xml b/google-cloud-firestore/clirr-ignored-differences.xml
index fd6e9e5b0e..a1064b15cf 100644
--- a/google-cloud-firestore/clirr-ignored-differences.xml
+++ b/google-cloud-firestore/clirr-ignored-differences.xml
@@ -314,4 +314,36 @@
com.google.cloud.firestore.Query getQuery()
+
+
+ 7002
+ com/google/cloud/firestore/FirestoreOpenTelemetryOptions
+ boolean isTracingEnabled()
+
+
+ 7002
+ com/google/cloud/firestore/FirestoreOpenTelemetryOptions$Builder
+ com.google.cloud.firestore.FirestoreOpenTelemetryOptions$Builder setTracingEnabled(boolean)
+
+
+
+ com/google/cloud/firestore/StreamableQuery
+ 7009
+ void internalStream(*)
+
+
+
+
+ 6011
+ com/google/cloud/firestore/telemetry/TraceUtil
+ SPAN_NAME_*
+
+
+
+
+ 7005
+ com/google/cloud/firestore/StreamableQuery
+ void internalStream(*)
+ *
+
diff --git a/google-cloud-firestore/pom.xml b/google-cloud-firestore/pom.xml
index c1399288fe..9e3894a63f 100644
--- a/google-cloud-firestore/pom.xml
+++ b/google-cloud-firestore/pom.xml
@@ -2,7 +2,7 @@
4.0.0
google-cloud-firestore
- 3.28.0
+ 3.29.0
jar
Google Cloud Firestore
https://github.com/googleapis/java-firestore
@@ -12,11 +12,11 @@
com.google.cloud
google-cloud-firestore-parent
- 3.28.0
+ 3.29.0
google-cloud-firestore
- 1.43.0
+ 1.44.1
@@ -119,6 +119,18 @@
io.opentelemetry.instrumentation
opentelemetry-grpc-1.6
+
+ io.opentelemetry
+ opentelemetry-sdk
+
+
+ io.opentelemetry
+ opentelemetry-sdk-metrics
+
+
+ com.google.cloud.opentelemetry
+ exporter-metrics
+
@@ -181,12 +193,6 @@
test
-
- io.opentelemetry
- opentelemetry-sdk
- ${opentelemetry.version}
- test
-
io.opentelemetry
opentelemetry-sdk-testing
@@ -225,20 +231,6 @@
2.53.0
test
-
- com.google.cloud.opentelemetry
- exporter-trace
- 0.33.0
- test
-
-
-
-
- com.google.api.grpc
- proto-google-cloud-trace-v1
- 2.53.0
- test
-
com.google.cloud
google-cloud-trace
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 84b4a04781..89702e4230 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
@@ -16,8 +16,8 @@
package com.google.cloud.firestore;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_RUN_AGGREGATION_QUERY;
import static com.google.cloud.firestore.telemetry.TraceUtil.ATTRIBUTE_KEY_ATTEMPT;
-import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_RUN_AGGREGATION_QUERY;
import com.google.api.core.ApiFuture;
import com.google.api.core.InternalExtensionOnly;
@@ -27,6 +27,9 @@
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.MetricsContext;
+import com.google.cloud.firestore.telemetry.TelemetryConstants;
+import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType;
import com.google.cloud.firestore.telemetry.TraceUtil;
import com.google.cloud.firestore.telemetry.TraceUtil.Scope;
import com.google.cloud.firestore.v1.FirestoreSettings;
@@ -70,6 +73,11 @@ private TraceUtil getTraceUtil() {
return query.getFirestore().getOptions().getTraceUtil();
}
+ @Nonnull
+ private MetricsContext createMetricsContext(String method) {
+ return query.getFirestore().getOptions().getMetricsUtil().createMetricsContext(method);
+ }
+
/** Returns the query whose aggregations will be calculated by this object. */
@Nonnull
public Query getQuery() {
@@ -96,14 +104,20 @@ public ApiFuture get() {
*/
@Nonnull
public ApiFuture> explain(ExplainOptions options) {
- TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_AGGREGATION_QUERY_GET);
+ TraceUtil.Span span =
+ getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_AGGREGATION_QUERY_GET);
+
+ MetricsContext metricsContext =
+ createMetricsContext(TelemetryConstants.METHOD_NAME_RUN_AGGREGATION_QUERY_EXPLAIN);
+
try (Scope ignored = span.makeCurrent()) {
AggregateQueryExplainResponseDeliverer responseDeliverer =
new AggregateQueryExplainResponseDeliverer(
/* transactionId= */ null,
/* readTime= */ null,
/* startTimeNanos= */ query.rpcContext.getClock().nanoTime(),
- /* explainOptions= */ options);
+ /* explainOptions= */ options,
+ metricsContext);
runQuery(responseDeliverer, /* attempt */ 0);
ApiFuture> result = responseDeliverer.getFuture();
span.endAtFuture(result);
@@ -121,14 +135,22 @@ ApiFuture get(
getTraceUtil()
.startSpan(
transactionId == null
- ? TraceUtil.SPAN_NAME_AGGREGATION_QUERY_GET
- : TraceUtil.SPAN_NAME_TRANSACTION_GET_AGGREGATION_QUERY);
+ ? TelemetryConstants.METHOD_NAME_AGGREGATION_QUERY_GET
+ : TelemetryConstants.METHOD_NAME_TRANSACTION_GET_AGGREGATION_QUERY);
+
+ MetricsContext metricsContext =
+ createMetricsContext(
+ transactionId == null
+ ? TelemetryConstants.METHOD_NAME_RUN_AGGREGATION_QUERY_GET
+ : TelemetryConstants.METHOD_NAME_RUN_AGGREGATION_QUERY_TRANSACTIONAL);
+
try (Scope ignored = span.makeCurrent()) {
AggregateQueryResponseDeliverer responseDeliverer =
new AggregateQueryResponseDeliverer(
transactionId,
readTime,
- /* startTimeNanos= */ query.rpcContext.getClock().nanoTime());
+ /* startTimeNanos= */ query.rpcContext.getClock().nanoTime(),
+ metricsContext);
runQuery(responseDeliverer, /* attempt= */ 0);
ApiFuture result = responseDeliverer.getFuture();
span.endAtFuture(result);
@@ -165,14 +187,17 @@ private abstract static class ResponseDeliverer {
private final @Nullable com.google.protobuf.Timestamp readTime;
private final long startTimeNanos;
private final SettableApiFuture future = SettableApiFuture.create();
+ private MetricsContext metricsContext;
ResponseDeliverer(
@Nullable ByteString transactionId,
@Nullable com.google.protobuf.Timestamp readTime,
- long startTimeNanos) {
+ long startTimeNanos,
+ MetricsContext metricsContext) {
this.transactionId = transactionId;
this.readTime = readTime;
this.startTimeNanos = startTimeNanos;
+ this.metricsContext = metricsContext;
}
@Nullable
@@ -198,15 +223,29 @@ ApiFuture getFuture() {
return future;
}
- protected void setFuture(T value) {
- future.set(value);
+ void deliverFirstResponse() {
+ metricsContext.recordLatency(MetricType.FIRST_RESPONSE_LATENCY);
}
void deliverError(Throwable throwable) {
future.setException(throwable);
+ metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, throwable);
}
- abstract void deliverResult(
+ void deliverResult(
+ @Nullable Map serverData,
+ Timestamp readTime,
+ @Nullable ExplainMetrics metrics) {
+ try {
+ T result = processResult(serverData, readTime, metrics);
+ future.set(result);
+ metricsContext.recordLatency(MetricType.END_TO_END_LATENCY);
+ } catch (Exception error) {
+ deliverError(error);
+ }
+ }
+
+ abstract T processResult(
@Nullable Map serverData,
Timestamp readTime,
@Nullable ExplainMetrics metrics);
@@ -216,24 +255,23 @@ private class AggregateQueryResponseDeliverer extends ResponseDeliverer serverData,
Timestamp readTime,
@Nullable ExplainMetrics metrics) {
if (serverData == null) {
- deliverError(new RuntimeException("Did not receive any aggregate query results."));
- return;
+ throw new RuntimeException("Did not receive any aggregate query results.");
}
- setFuture(
- new AggregateQuerySnapshot(
- AggregateQuery.this,
- readTime,
- convertServerAggregateFieldsMapToClientAggregateFieldsMap(serverData)));
+ return new AggregateQuerySnapshot(
+ AggregateQuery.this,
+ readTime,
+ convertServerAggregateFieldsMapToClientAggregateFieldsMap(serverData));
}
}
@@ -245,8 +283,9 @@ private final class AggregateQueryExplainResponseDeliverer
@Nullable ByteString transactionId,
@Nullable com.google.protobuf.Timestamp readTime,
long startTimeNanos,
- @Nullable ExplainOptions explainOptions) {
- super(transactionId, readTime, startTimeNanos);
+ @Nullable ExplainOptions explainOptions,
+ MetricsContext metricsContext) {
+ super(transactionId, readTime, startTimeNanos, metricsContext);
this.explainOptions = explainOptions;
}
@@ -257,14 +296,13 @@ ExplainOptions getExplainOptions() {
}
@Override
- void deliverResult(
+ ExplainResults processResult(
@Nullable Map serverData,
Timestamp readTime,
@Nullable ExplainMetrics metrics) {
// The server is required to provide ExplainMetrics for explain queries.
if (metrics == null) {
- deliverError(new RuntimeException("Did not receive any metrics for explain query."));
- return;
+ throw new RuntimeException("Did not receive any metrics for explain query.");
}
AggregateQuerySnapshot snapshot =
serverData == null
@@ -273,7 +311,7 @@ void deliverResult(
AggregateQuery.this,
readTime,
convertServerAggregateFieldsMapToClientAggregateFieldsMap(serverData));
- setFuture(new ExplainResults<>(metrics, snapshot));
+ return new ExplainResults<>(metrics, snapshot);
}
}
@@ -284,6 +322,7 @@ private final class AggregateQueryResponseObserver
@Nullable private Map aggregateFieldsMap = null;
@Nullable private ExplainMetrics metrics = null;
private int attempt;
+ private boolean firstResponse = false;
AggregateQueryResponseObserver(ResponseDeliverer responseDeliverer, int attempt) {
this.responseDeliverer = responseDeliverer;
@@ -302,15 +341,20 @@ private boolean isExplainQuery() {
public void onStart(StreamController streamController) {
getTraceUtil()
.currentSpan()
- .addEvent(SPAN_NAME_RUN_AGGREGATION_QUERY + " Stream started.", getAttemptAttributes());
+ .addEvent(METHOD_NAME_RUN_AGGREGATION_QUERY + " Stream started.", getAttemptAttributes());
}
@Override
public void onResponse(RunAggregationQueryResponse response) {
+ if (!firstResponse) {
+ firstResponse = true;
+ responseDeliverer.deliverFirstResponse();
+ }
+
getTraceUtil()
.currentSpan()
.addEvent(
- SPAN_NAME_RUN_AGGREGATION_QUERY + " Response Received.", getAttemptAttributes());
+ METHOD_NAME_RUN_AGGREGATION_QUERY + " Response Received.", getAttemptAttributes());
if (response.hasReadTime()) {
readTime = Timestamp.fromProto(response.getReadTime());
}
@@ -339,16 +383,16 @@ public void onError(Throwable throwable) {
getTraceUtil()
.currentSpan()
.addEvent(
- SPAN_NAME_RUN_AGGREGATION_QUERY + ": Retryable Error",
- Collections.singletonMap("error.message", throwable.getMessage()));
+ METHOD_NAME_RUN_AGGREGATION_QUERY + ": Retryable Error",
+ Collections.singletonMap("error.message", throwable.toString()));
runQuery(responseDeliverer, attempt + 1);
} else {
getTraceUtil()
.currentSpan()
.addEvent(
- SPAN_NAME_RUN_AGGREGATION_QUERY + ": Error",
- Collections.singletonMap("error.message", throwable.getMessage()));
+ METHOD_NAME_RUN_AGGREGATION_QUERY + ": Error",
+ Collections.singletonMap("error.message", throwable.toString()));
responseDeliverer.deliverError(throwable);
}
}
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 bc881979c0..0a713fe401 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,9 @@
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.TelemetryConstants;
+import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType;
import com.google.cloud.firestore.telemetry.TraceUtil;
import com.google.cloud.firestore.telemetry.TraceUtil.Context;
import com.google.cloud.firestore.telemetry.TraceUtil.Scope;
@@ -912,8 +915,15 @@ private void sendBatchLocked(final BulkCommitBatch batch, final boolean flush) {
firestore
.getOptions()
.getTraceUtil()
- .startSpan(TraceUtil.SPAN_NAME_BULK_WRITER_COMMIT, traceContext)
+ .startSpan(TelemetryConstants.METHOD_NAME_BULK_WRITER_COMMIT, traceContext)
.setAttribute(ATTRIBUTE_KEY_DOC_COUNT, batch.getMutationsSize());
+
+ MetricsContext metricsContext =
+ firestore
+ .getOptions()
+ .getMetricsUtil()
+ .createMetricsContext(TelemetryConstants.METHOD_NAME_BULK_WRITER_COMMIT);
+
try (Scope ignored = span.makeCurrent()) {
ApiFuture result = batch.bulkCommit();
result.addListener(
@@ -926,8 +936,10 @@ private void sendBatchLocked(final BulkCommitBatch batch, final boolean flush) {
},
bulkWriterExecutor);
span.endAtFuture(result);
+ metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result);
} catch (Exception error) {
span.end(error);
+ metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, 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 d81a0489ea..159f7077d8 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,9 @@
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.TelemetryConstants;
+import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType;
import com.google.cloud.firestore.telemetry.TraceUtil;
import com.google.cloud.firestore.telemetry.TraceUtil.Scope;
import com.google.cloud.firestore.v1.FirestoreClient.PartitionQueryPagedResponse;
@@ -109,7 +112,15 @@ public ApiFuture> getPartitions(long desiredPartitionCount)
.getFirestore()
.getOptions()
.getTraceUtil()
- .startSpan(TraceUtil.SPAN_NAME_PARTITION_QUERY);
+ .startSpan(TelemetryConstants.METHOD_NAME_PARTITION_QUERY);
+
+ MetricsContext metricsContext =
+ rpcContext
+ .getFirestore()
+ .getOptions()
+ .getMetricsUtil()
+ .createMetricsContext(TelemetryConstants.METHOD_NAME_PARTITION_QUERY);
+
try (Scope ignored = span.makeCurrent()) {
ApiFuture> result =
ApiFutures.transform(
@@ -127,12 +138,15 @@ public ApiFuture> getPartitions(long desiredPartitionCount)
},
MoreExecutors.directExecutor());
span.endAtFuture(result);
+ metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result);
return result;
} catch (ApiException exception) {
span.end(exception);
+ metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, exception);
throw FirestoreException.forApiException(exception);
} catch (Throwable throwable) {
span.end(throwable);
+ metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, 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 e1c2841d68..75954d82de 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
@@ -24,6 +24,9 @@
import com.google.api.gax.rpc.UnaryCallable;
import com.google.cloud.firestore.encoding.CustomClassMapper;
import com.google.cloud.firestore.spi.v1.FirestoreRpc;
+import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext;
+import com.google.cloud.firestore.telemetry.TelemetryConstants;
+import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType;
import com.google.cloud.firestore.telemetry.TraceUtil;
import com.google.cloud.firestore.telemetry.TraceUtil.Scope;
import com.google.cloud.firestore.v1.FirestoreClient.ListDocumentsPagedResponse;
@@ -134,7 +137,15 @@ public Iterable listDocuments() {
.getFirestore()
.getOptions()
.getTraceUtil()
- .startSpan(TraceUtil.SPAN_NAME_COL_REF_LIST_DOCUMENTS);
+ .startSpan(TelemetryConstants.METHOD_NAME_COL_REF_LIST_DOCUMENTS);
+
+ MetricsContext metricsContext =
+ rpcContext
+ .getFirestore()
+ .getOptions()
+ .getMetricsUtil()
+ .createMetricsContext(TelemetryConstants.METHOD_NAME_COL_REF_LIST_DOCUMENTS);
+
try (Scope ignored = span.makeCurrent()) {
ListDocumentsRequest.Builder request = ListDocumentsRequest.newBuilder();
request.setParent(options.getParentPath().toString());
@@ -174,12 +185,15 @@ public void remove() {
}
};
span.end();
+ metricsContext.recordLatency(MetricType.END_TO_END_LATENCY);
return result;
} catch (ApiException exception) {
span.end(exception);
+ metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, exception);
throw FirestoreException.forApiException(exception);
} catch (Throwable throwable) {
span.end(throwable);
+ metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, throwable);
throw throwable;
}
}
@@ -200,7 +214,15 @@ public ApiFuture add(@Nonnull final Map field
.getFirestore()
.getOptions()
.getTraceUtil()
- .startSpan(TraceUtil.SPAN_NAME_COL_REF_ADD);
+ .startSpan(TelemetryConstants.METHOD_NAME_COL_REF_ADD);
+
+ MetricsContext metricsContext =
+ rpcContext
+ .getFirestore()
+ .getOptions()
+ .getMetricsUtil()
+ .createMetricsContext(TelemetryConstants.METHOD_NAME_COL_REF_ADD);
+
try (Scope ignored = span.makeCurrent()) {
final DocumentReference documentReference = document();
ApiFuture createFuture = documentReference.create(fields);
@@ -208,9 +230,11 @@ public ApiFuture add(@Nonnull final Map field
ApiFutures.transform(
createFuture, writeResult -> documentReference, MoreExecutors.directExecutor());
span.endAtFuture(result);
+ metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result);
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, 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 57254bb2b6..2b0cc1ddc9 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,10 @@
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.TelemetryConstants;
+import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType;
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 +143,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.
@@ -148,14 +157,20 @@ private TraceUtil getTraceUtil() {
*/
@Nonnull
public ApiFuture create(@Nonnull Map fields) {
- TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_CREATE);
+ TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_CREATE);
+
+ MetricsContext metricsContext =
+ getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_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.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result);
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error);
throw error;
}
}
@@ -169,14 +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);
+ TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_CREATE);
+ MetricsContext metricsContext =
+ getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_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.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result);
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error);
throw error;
}
}
@@ -190,14 +210,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);
+ TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_SET);
+ MetricsContext metricsContext =
+ getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_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.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result);
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error);
throw error;
}
}
@@ -214,14 +239,19 @@ public ApiFuture set(@Nonnull Map fields) {
@Nonnull
public ApiFuture set(
@Nonnull Map fields, @Nonnull SetOptions options) {
- TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_SET);
+ TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_SET);
+ MetricsContext metricsContext =
+ getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_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.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result);
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error);
throw error;
}
}
@@ -235,14 +265,19 @@ public ApiFuture set(
*/
@Nonnull
public ApiFuture set(@Nonnull Object pojo) {
- TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_SET);
+ TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_SET);
+ MetricsContext metricsContext =
+ getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_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.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result);
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error);
throw error;
}
}
@@ -258,14 +293,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);
+ TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_SET);
+ MetricsContext metricsContext =
+ getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_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.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result);
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error);
throw error;
}
}
@@ -279,14 +319,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);
+ TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE);
+ MetricsContext metricsContext =
+ getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_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.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result);
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error);
throw error;
}
}
@@ -301,15 +346,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);
+ TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE);
+ MetricsContext metricsContext =
+ getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_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.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result);
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error);
throw error;
}
}
@@ -326,15 +376,20 @@ public ApiFuture update(@Nonnull Map fields, Precon
@Nonnull
public ApiFuture update(
@Nonnull String field, @Nullable Object value, Object... moreFieldsAndValues) {
- TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_UPDATE);
+ TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE);
+ MetricsContext metricsContext =
+ getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_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.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result);
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error);
throw error;
}
}
@@ -351,15 +406,20 @@ public ApiFuture update(
@Nonnull
public ApiFuture update(
@Nonnull FieldPath fieldPath, @Nullable Object value, Object... moreFieldsAndValues) {
- TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_UPDATE);
+ TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE);
+ MetricsContext metricsContext =
+ getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_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.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result);
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error);
throw error;
}
}
@@ -380,16 +440,21 @@ public ApiFuture update(
@Nonnull String field,
@Nullable Object value,
Object... moreFieldsAndValues) {
- TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_UPDATE);
+ TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE);
+ MetricsContext metricsContext =
+ getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_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.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result);
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error);
throw error;
}
}
@@ -410,16 +475,21 @@ public ApiFuture update(
@Nonnull FieldPath fieldPath,
@Nullable Object value,
Object... moreFieldsAndValues) {
- TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_UPDATE);
+ TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE);
+ MetricsContext metricsContext =
+ getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_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.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result);
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error);
throw error;
}
}
@@ -432,14 +502,19 @@ public ApiFuture update(
*/
@Nonnull
public ApiFuture delete(@Nonnull Precondition options) {
- TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_DELETE);
+ TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_DELETE);
+ MetricsContext metricsContext =
+ getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_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.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result);
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error);
throw error;
}
}
@@ -451,14 +526,19 @@ public ApiFuture delete(@Nonnull Precondition options) {
*/
@Nonnull
public ApiFuture delete() {
- TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_DELETE);
+ TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_DELETE);
+ MetricsContext metricsContext =
+ getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_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.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result);
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error);
throw error;
}
}
@@ -472,13 +552,18 @@ public ApiFuture delete() {
*/
@Nonnull
public ApiFuture get() {
- TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_GET);
+ TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_GET);
+ MetricsContext metricsContext =
+ getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_GET);
+
try (Scope ignored = span.makeCurrent()) {
ApiFuture result = extractFirst(rpcContext.getFirestore().getAll(this));
span.endAtFuture(result);
+ metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result);
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error);
throw error;
}
}
@@ -493,14 +578,19 @@ public ApiFuture get() {
*/
@Nonnull
public ApiFuture get(FieldMask fieldMask) {
- TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_GET);
+ TraceUtil.Span span = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_GET);
+ MetricsContext metricsContext =
+ getMetricsUtil().createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_GET);
+
try (Scope ignored = span.makeCurrent()) {
ApiFuture result =
extractFirst(rpcContext.getFirestore().getAll(new DocumentReference[] {this}, fieldMask));
span.endAtFuture(result);
+ metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result);
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error);
throw error;
}
}
@@ -513,7 +603,12 @@ public ApiFuture get(FieldMask fieldMask) {
*/
@Nonnull
public Iterable listCollections() {
- TraceUtil.Span span = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_DOC_REF_LIST_COLLECTIONS);
+ TraceUtil.Span span =
+ getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_DOC_REF_LIST_COLLECTIONS);
+ MetricsContext metricsContext =
+ getMetricsUtil()
+ .createMetricsContext(TelemetryConstants.METHOD_NAME_DOC_REF_LIST_COLLECTIONS);
+
try (Scope ignored = span.makeCurrent()) {
ListCollectionIdsRequest.Builder request = ListCollectionIdsRequest.newBuilder();
request.setParent(path.toString());
@@ -547,9 +642,11 @@ public void remove() {
}
};
span.end();
+ metricsContext.recordLatency(MetricType.END_TO_END_LATENCY);
return result;
} catch (ApiException exception) {
span.end(exception);
+ metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, exception);
throw FirestoreException.forApiException(exception);
}
}
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 48c691466e..5c72171412 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,9 @@
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.MetricsContext;
+import com.google.cloud.firestore.telemetry.TelemetryConstants;
+import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType;
import com.google.cloud.firestore.telemetry.TraceUtil;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Preconditions;
@@ -227,6 +230,14 @@ void getAll(
// that we receive from the server.
final int NUM_RESPONSES_PER_TRACE_EVENT = 100;
+ MetricsContext metricsContext =
+ getOptions()
+ .getMetricsUtil()
+ .createMetricsContext(
+ transactionId == null
+ ? TelemetryConstants.METHOD_NAME_BATCH_GET_DOCUMENTS_GET_ALL
+ : TelemetryConstants.METHOD_NAME_BATCH_GET_DOCUMENTS_TRANSACTIONAL);
+
ResponseObserver responseObserver =
new ResponseObserver() {
int numResponses = 0;
@@ -237,7 +248,7 @@ public void onStart(StreamController streamController) {
getTraceUtil()
.currentSpan()
.addEvent(
- TraceUtil.SPAN_NAME_BATCH_GET_DOCUMENTS + ": Start",
+ TelemetryConstants.METHOD_NAME_BATCH_GET_DOCUMENTS + ": Start",
new ImmutableMap.Builder()
.put(ATTRIBUTE_KEY_DOC_COUNT, documentReferences.length)
.put(ATTRIBUTE_KEY_IS_TRANSACTIONAL, transactionId != null)
@@ -253,12 +264,15 @@ public void onResponse(BatchGetDocumentsResponse response) {
if (numResponses == 1) {
getTraceUtil()
.currentSpan()
- .addEvent(TraceUtil.SPAN_NAME_BATCH_GET_DOCUMENTS + ": First response received");
+ .addEvent(
+ TelemetryConstants.METHOD_NAME_BATCH_GET_DOCUMENTS
+ + ": First response received");
+ metricsContext.recordLatency(MetricType.FIRST_RESPONSE_LATENCY);
} else if (numResponses % NUM_RESPONSES_PER_TRACE_EVENT == 0) {
getTraceUtil()
.currentSpan()
.addEvent(
- TraceUtil.SPAN_NAME_BATCH_GET_DOCUMENTS
+ TelemetryConstants.METHOD_NAME_BATCH_GET_DOCUMENTS
+ ": Received "
+ numResponses
+ " responses");
@@ -298,6 +312,7 @@ public void onResponse(BatchGetDocumentsResponse response) {
@Override
public void onError(Throwable throwable) {
getTraceUtil().currentSpan().end(throwable);
+ metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, throwable);
apiStreamObserver.onError(throwable);
}
@@ -308,11 +323,12 @@ public void onComplete() {
getTraceUtil()
.currentSpan()
.addEvent(
- TraceUtil.SPAN_NAME_BATCH_GET_DOCUMENTS
+ TelemetryConstants.METHOD_NAME_BATCH_GET_DOCUMENTS
+ ": Completed with "
+ numResponses
+ " responses.",
Collections.singletonMap(ATTRIBUTE_KEY_NUM_RESPONSES, numResponses));
+ metricsContext.recordLatency(MetricType.END_TO_END_LATENCY);
apiStreamObserver.onCompleted();
}
};
@@ -427,16 +443,30 @@ public ApiFuture runAsyncTransaction(
@Nonnull final Transaction.AsyncFunction updateFunction,
@Nonnull TransactionOptions transactionOptions) {
- if (transactionOptions.getReadTime() != null) {
- // READ_ONLY transactions with readTime have no retry, nor transaction state, so we don't need
- // a runner.
- return updateFunction.updateCallback(
- new ReadTimeTransaction(this, transactionOptions.getReadTime()));
- } else {
- // For READ_ONLY transactions without readTime, there is still strong consistency applied,
- // that cannot be tracked client side.
- return new ServerSideTransactionRunner<>(this, updateFunction, transactionOptions).run();
+ MetricsContext metricsContext =
+ getOptions()
+ .getMetricsUtil()
+ .createMetricsContext(TelemetryConstants.METHOD_NAME_RUN_TRANSACTION);
+ ApiFuture result;
+
+ try {
+ if (transactionOptions.getReadTime() != null) {
+ // READ_ONLY transactions with readTime have no retry, nor transaction state, so we don't
+ // need a runner.
+ result =
+ updateFunction.updateCallback(
+ new ReadTimeTransaction(this, transactionOptions.getReadTime()));
+ } else {
+ // For READ_ONLY transactions without readTime, there is still strong consistency applied,
+ // that cannot be tracked client side.
+ result = new ServerSideTransactionRunner<>(this, updateFunction, transactionOptions).run();
+ }
+ metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result);
+ } catch (Exception error) {
+ metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error);
+ throw error;
}
+ return result;
}
@Nonnull
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 d82a710523..6fe0c4375a 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
@@ -27,23 +27,17 @@
*/
@BetaApi
public class FirestoreOpenTelemetryOptions {
- private final boolean tracingEnabled;
+ private final boolean exportBuiltinMetricsToGoogleCloudMonitoring;
private final @Nullable OpenTelemetry openTelemetry;
FirestoreOpenTelemetryOptions(Builder builder) {
- this.tracingEnabled = builder.tracingEnabled;
+ this.exportBuiltinMetricsToGoogleCloudMonitoring =
+ builder.exportBuiltinMetricsToGoogleCloudMonitoring;
this.openTelemetry = builder.openTelemetry;
}
- /**
- * @deprecated This method will be removed in the next minor version update. Please use a no-op
- * TracerProvider or set the environment variable `FIRESTORE_ENABLE_TRACING=OFF` to disable
- * tracing. If the GlobalOpenTelemetry or the OpenTelemetry instance passed to Firestore
- * contain a valid TracerProvider, the Firestore client will generate spans by utilizing it.
- */
- @Deprecated
- public boolean isTracingEnabled() {
- return tracingEnabled;
+ public boolean exportBuiltinMetricsToGoogleCloudMonitoring() {
+ return exportBuiltinMetricsToGoogleCloudMonitoring;
}
public OpenTelemetry getOpenTelemetry() {
@@ -61,18 +55,18 @@ public static FirestoreOpenTelemetryOptions.Builder newBuilder() {
}
public static class Builder {
-
- private boolean tracingEnabled;
-
+ private boolean exportBuiltinMetricsToGoogleCloudMonitoring;
@Nullable private OpenTelemetry openTelemetry;
private Builder() {
- tracingEnabled = false;
+ // TODO(metrics): default this to true when feature is ready
+ exportBuiltinMetricsToGoogleCloudMonitoring = false;
openTelemetry = null;
}
private Builder(FirestoreOpenTelemetryOptions options) {
- this.tracingEnabled = options.tracingEnabled;
+ this.exportBuiltinMetricsToGoogleCloudMonitoring =
+ options.exportBuiltinMetricsToGoogleCloudMonitoring;
this.openTelemetry = options.openTelemetry;
}
@@ -81,20 +75,17 @@ public FirestoreOpenTelemetryOptions build() {
return new FirestoreOpenTelemetryOptions(this);
}
+ // TODO(metrics): make this public when feature is ready.
/**
- * Sets whether tracing should be enabled.
+ * Sets whether built-in metrics should be exported to Google Cloud Monitoring
*
- * @param tracingEnabled Whether tracing should be enabled.
- * @deprecated This method will be removed in the next minor version update. Please use a no-op
- * TracerProvider or set the environment variable `FIRESTORE_ENABLE_TRACING=OFF` to disable
- * tracing. If the GlobalOpenTelemetry or the OpenTelemetry instance passed to Firestore
- * contains a valid TracerProvider, the Firestore client will generate spans by utilizing
- * it.
+ * @param exportBuiltinMetrics Whether built-in metrics should be exported to Google Cloud
+ * Monitoring.
*/
- @Deprecated
@Nonnull
- public FirestoreOpenTelemetryOptions.Builder setTracingEnabled(boolean tracingEnabled) {
- this.tracingEnabled = tracingEnabled;
+ private FirestoreOpenTelemetryOptions.Builder exportBuiltinMetricsToGoogleCloudMonitoring(
+ boolean exportBuiltinMetrics) {
+ this.exportBuiltinMetricsToGoogleCloudMonitoring = exportBuiltinMetrics;
return this;
}
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 b80b9dacdc..b0887f602a 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
@@ -24,12 +24,15 @@
import com.google.api.gax.core.GoogleCredentialsProvider;
import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider;
import com.google.api.gax.rpc.TransportChannelProvider;
+import com.google.api.gax.tracing.ApiTracerFactory;
import com.google.auth.Credentials;
import com.google.cloud.ServiceDefaults;
import com.google.cloud.ServiceOptions;
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.CompositeApiTracerFactory;
+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;
@@ -37,6 +40,7 @@
import io.grpc.ManagedChannelBuilder;
import java.io.IOException;
import java.net.URI;
+import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
@@ -64,6 +68,7 @@ public final class FirestoreOptions extends ServiceOptions apiTracerFactories = new ArrayList<>();
+ // Prefer any direct ApiTracerFactory that might have been set on the builder.
+ if (super.getApiTracerFactory() != null) {
+ apiTracerFactories.add(super.getApiTracerFactory());
+ }
+ // Add Metrics Tracer factory if built-in metrics are enabled.
+ metricsUtil.addMetricsTracerFactory(apiTracerFactories);
+
+ if (apiTracerFactories.isEmpty()) {
+ return null;
+ }
+ return new CompositeApiTracerFactory(apiTracerFactories);
+ }
+
public String getDatabaseId() {
return databaseId;
}
@@ -127,6 +149,11 @@ com.google.cloud.firestore.telemetry.TraceUtil getTraceUtil() {
return traceUtil;
}
+ @Nonnull
+ com.google.cloud.firestore.telemetry.MetricsUtil getMetricsUtil() {
+ return metricsUtil;
+ }
+
@BetaApi
@Nonnull
public FirestoreOpenTelemetryOptions getOpenTelemetryOptions() {
@@ -325,6 +352,9 @@ protected FirestoreOptions(Builder builder) {
? builder.databaseId
: FirestoreDefaults.INSTANCE.getDatabaseId();
+ // Set up the `MetricsUtil` instance after the database ID has been set.
+ this.metricsUtil = MetricsUtil.getInstance(this);
+
if (builder.channelProvider == null) {
ApiFunction channelConfigurator =
this.traceUtil.getChannelConfigurator();
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 e66393b848..f93ec2c29f 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
@@ -35,6 +35,8 @@
import com.google.cloud.Timestamp;
import com.google.cloud.firestore.Query.QueryOptions.Builder;
import com.google.cloud.firestore.encoding.CustomClassMapper;
+import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext;
+import com.google.cloud.firestore.telemetry.TelemetryConstants;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.firestore.bundle.BundledQuery;
@@ -1487,7 +1489,10 @@ public void stream(@Nonnull final ApiStreamObserver responseOb
"Query results for queries that include limitToLast() constraints cannot be streamed. "
+ "Use Query.get() instead.");
- internalStream(
+ MetricsContext metricsContext =
+ createMetricsContext(TelemetryConstants.METHOD_NAME_RUN_QUERY_GET);
+
+ ApiStreamObserver observer =
new ApiStreamObserver() {
@Override
public void onNext(RunQueryResponse runQueryResponse) {
@@ -1509,7 +1514,10 @@ public void onError(Throwable throwable) {
public void onCompleted() {
responseObserver.onCompleted();
}
- },
+ };
+
+ internalStream(
+ new MonitoredStreamResponseObserver(observer, metricsContext),
/* startTimeNanos= */ rpcContext.getClock().nanoTime(),
/* transactionId= */ null,
/* readTime= */ null,
@@ -1533,8 +1541,12 @@ public ApiFuture explainStream(
"Query results for queries that include limitToLast() constraints cannot be streamed. "
+ "Use Query.explain() instead.");
+ MetricsContext metricsContext =
+ createMetricsContext(TelemetryConstants.METHOD_NAME_RUN_QUERY_EXPLAIN);
+
final SettableApiFuture metricsFuture = SettableApiFuture.create();
- internalStream(
+
+ ApiStreamObserver observer =
new ApiStreamObserver() {
@Override
public void onNext(RunQueryResponse runQueryResponse) {
@@ -1566,7 +1578,10 @@ public void onCompleted() {
new RuntimeException("Did not receive any explain results."));
}
}
- },
+ };
+
+ internalStream(
+ new MonitoredStreamResponseObserver(observer, metricsContext),
/* startTimeNanos= */ rpcContext.getClock().nanoTime(),
/* transactionId= */ null,
/* readTime= */ null,
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 0c423469af..8780e9c632 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.TelemetryConstants;
import com.google.cloud.firestore.telemetry.TraceUtil;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.MoreExecutors;
@@ -57,7 +58,8 @@ public boolean hasTransactionId() {
public ApiFuture get(@Nonnull DocumentReference documentRef) {
TraceUtil.Span span =
getTraceUtil()
- .startSpan(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENT, transactionTraceContext);
+ .startSpan(
+ TelemetryConstants.METHOD_NAME_TRANSACTION_GET_DOCUMENT, transactionTraceContext);
try (TraceUtil.Scope ignored = span.makeCurrent()) {
ApiFuture result =
ApiFutures.transform(
@@ -79,7 +81,8 @@ public ApiFuture> getAll(
@Nonnull DocumentReference... documentReferences) {
TraceUtil.Span span =
getTraceUtil()
- .startSpan(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENTS, transactionTraceContext);
+ .startSpan(
+ TelemetryConstants.METHOD_NAME_TRANSACTION_GET_DOCUMENTS, transactionTraceContext);
try (TraceUtil.Scope ignored = span.makeCurrent()) {
ApiFuture> result =
firestore.getAll(documentReferences, /* fieldMask= */ null, readTime);
@@ -97,7 +100,8 @@ public ApiFuture> getAll(
@Nonnull DocumentReference[] documentReferences, @Nullable FieldMask fieldMask) {
TraceUtil.Span span =
getTraceUtil()
- .startSpan(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENTS, transactionTraceContext);
+ .startSpan(
+ TelemetryConstants.METHOD_NAME_TRANSACTION_GET_DOCUMENTS, transactionTraceContext);
try (TraceUtil.Scope ignored = span.makeCurrent()) {
ApiFuture> result =
firestore.getAll(documentReferences, /* fieldMask= */ null, readTime);
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 5d366c965f..40dd64ce88 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.TelemetryConstants;
import com.google.cloud.firestore.telemetry.TraceUtil;
import com.google.common.base.Preconditions;
import com.google.common.util.concurrent.MoreExecutors;
@@ -108,7 +109,9 @@ ApiFuture> commit() {
/** Rolls a transaction back and releases all read locks. */
ApiFuture rollback() {
TraceUtil.Span span =
- getTraceUtil().startSpan(TraceUtil.SPAN_NAME_TRANSACTION_ROLLBACK, transactionTraceContext);
+ getTraceUtil()
+ .startSpan(
+ TelemetryConstants.METHOD_NAME_TRANSACTION_ROLLBACK, transactionTraceContext);
try (TraceUtil.Scope ignored = span.makeCurrent()) {
RollbackRequest req =
RollbackRequest.newBuilder()
@@ -158,7 +161,8 @@ public boolean hasTransactionId() {
public ApiFuture get(@Nonnull DocumentReference documentRef) {
TraceUtil.Span span =
getTraceUtil()
- .startSpan(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENT, transactionTraceContext);
+ .startSpan(
+ TelemetryConstants.METHOD_NAME_TRANSACTION_GET_DOCUMENT, transactionTraceContext);
try (TraceUtil.Scope ignored = span.makeCurrent()) {
Preconditions.checkState(isEmpty(), READ_BEFORE_WRITE_ERROR_MSG);
ApiFuture result =
@@ -191,7 +195,8 @@ public ApiFuture> getAll(
Preconditions.checkState(isEmpty(), READ_BEFORE_WRITE_ERROR_MSG);
TraceUtil.Span span =
getTraceUtil()
- .startSpan(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENTS, transactionTraceContext);
+ .startSpan(
+ TelemetryConstants.METHOD_NAME_TRANSACTION_GET_DOCUMENTS, transactionTraceContext);
try (TraceUtil.Scope ignored = span.makeCurrent()) {
ApiFuture> result =
firestore.getAll(
@@ -219,7 +224,8 @@ public ApiFuture> getAll(
Preconditions.checkState(isEmpty(), READ_BEFORE_WRITE_ERROR_MSG);
TraceUtil.Span span =
getTraceUtil()
- .startSpan(TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENTS, transactionTraceContext);
+ .startSpan(
+ TelemetryConstants.METHOD_NAME_TRANSACTION_GET_DOCUMENTS, transactionTraceContext);
try (TraceUtil.Scope ignored = span.makeCurrent()) {
ApiFuture> result =
firestore.getAll(documentReferences, fieldMask, transactionId, /* readTime= */ null);
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java
index db8ebff635..29c92331a3 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/ServerSideTransactionRunner.java
@@ -26,6 +26,9 @@
import com.google.api.gax.retrying.ExponentialRetryAlgorithm;
import com.google.api.gax.retrying.TimedAttemptSettings;
import com.google.api.gax.rpc.ApiException;
+import com.google.cloud.firestore.telemetry.MetricsUtil.MetricsContext;
+import com.google.cloud.firestore.telemetry.TelemetryConstants;
+import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType;
import com.google.cloud.firestore.telemetry.TraceUtil;
import com.google.cloud.firestore.telemetry.TraceUtil.Scope;
import com.google.cloud.firestore.telemetry.TraceUtil.Span;
@@ -59,6 +62,7 @@ final class ServerSideTransactionRunner {
private int attemptsRemaining;
private Span runTransactionSpan;
private TraceUtil.Context runTransactionContext;
+ private MetricsContext metricsContext;
/**
* @param firestore The active Firestore instance
@@ -85,6 +89,11 @@ final class ServerSideTransactionRunner {
new ExponentialRetryAlgorithm(
firestore.getOptions().getRetrySettings(), CurrentMillisClock.getDefaultClock());
this.nextBackoffAttempt = backoffAlgorithm.createFirstAttempt();
+ this.metricsContext =
+ firestore
+ .getOptions()
+ .getMetricsUtil()
+ .createMetricsContext(TelemetryConstants.METHOD_NAME_TRANSACTION_RUN);
}
@Nonnull
@@ -93,7 +102,14 @@ private TraceUtil getTraceUtil() {
}
ApiFuture run() {
- runTransactionSpan = getTraceUtil().startSpan(TraceUtil.SPAN_NAME_TRANSACTION_RUN);
+ ApiFuture result = runInternally();
+ metricsContext.recordLatencyAtFuture(MetricType.TRANSACTION_LATENCY, result);
+ metricsContext.recordCounterAtFuture(MetricType.TRANSACTION_ATTEMPT_COUNT, result);
+ return result;
+ }
+
+ ApiFuture runInternally() {
+ runTransactionSpan = getTraceUtil().startSpan(TelemetryConstants.METHOD_NAME_TRANSACTION_RUN);
runTransactionSpan.setAttribute(
ATTRIBUTE_KEY_TRANSACTION_TYPE, transactionOptions.getType().name());
runTransactionSpan.setAttribute(
@@ -102,6 +118,7 @@ ApiFuture run() {
try (Scope ignored = runTransactionSpan.makeCurrent()) {
runTransactionContext = getTraceUtil().currentContext();
--attemptsRemaining;
+ metricsContext.incrementCounter();
ApiFuture result =
ApiFutures.catchingAsync(
ApiFutures.transformAsync(
@@ -119,7 +136,8 @@ ApiFuture run() {
ApiFuture begin() {
TraceUtil.Span span =
- getTraceUtil().startSpan(TraceUtil.SPAN_NAME_TRANSACTION_BEGIN, runTransactionContext);
+ getTraceUtil()
+ .startSpan(TelemetryConstants.METHOD_NAME_TRANSACTION_BEGIN, runTransactionContext);
try (Scope ignored = span.makeCurrent()) {
ServerSideTransaction previousTransaction = this.transaction;
this.transaction = null;
@@ -235,7 +253,7 @@ private ApiFuture restartTransactionCallback(Throwable throwable) {
getTraceUtil()
.currentSpan()
.addEvent("Initiating transaction retry. Attempts remaining: " + attemptsRemaining);
- return run();
+ return runInternally();
} else {
final FirestoreException firestoreException =
FirestoreException.forApiException(
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/StreamableQuery.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/StreamableQuery.java
index bf66a41abf..a959f9a153 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/StreamableQuery.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/StreamableQuery.java
@@ -20,13 +20,18 @@
import static com.google.common.collect.Lists.reverse;
import com.google.api.core.ApiFuture;
+import com.google.api.core.InternalApi;
import com.google.api.core.SettableApiFuture;
import com.google.api.gax.rpc.ApiStreamObserver;
import com.google.api.gax.rpc.ResponseObserver;
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.MetricsContext;
+import com.google.cloud.firestore.telemetry.TelemetryConstants;
+import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType;
import com.google.cloud.firestore.telemetry.TraceUtil;
+import com.google.cloud.firestore.telemetry.TraceUtil.Scope;
import com.google.cloud.firestore.v1.FirestoreSettings;
import com.google.common.collect.ImmutableMap;
import com.google.firestore.v1.Document;
@@ -49,6 +54,7 @@
* `isRetryableWithCursor`. Retrying with a cursor means that the StreamableQuery can be resumed
* where it failed by first calling `startAfter(lastDocumentReceived)`.
*/
+@InternalApi
public abstract class StreamableQuery {
final Query.QueryOptions options;
final FirestoreRpcContext> rpcContext;
@@ -79,6 +85,10 @@ abstract SnapshotType createSnaphot(
public Firestore getFirestore() {
return rpcContext.getFirestore();
}
+
+ MetricsContext createMetricsContext(String methodName) {
+ return getFirestore().getOptions().getMetricsUtil().createMetricsContext(methodName);
+ }
/**
* Executes the query and returns the results as QuerySnapshot.
*
@@ -100,11 +110,19 @@ ApiFuture get(
.getTraceUtil()
.startSpan(
transactionId == null
- ? TraceUtil.SPAN_NAME_QUERY_GET
- : TraceUtil.SPAN_NAME_TRANSACTION_GET_QUERY);
+ ? TelemetryConstants.METHOD_NAME_QUERY_GET
+ : TelemetryConstants.METHOD_NAME_TRANSACTION_GET_QUERY);
+
+ MetricsContext metricsContext =
+ createMetricsContext(
+ transactionId != null
+ ? TelemetryConstants.METHOD_NAME_RUN_QUERY_TRANSACTIONAL
+ : TelemetryConstants.METHOD_NAME_RUN_QUERY_GET);
+
try (Scope ignored = span.makeCurrent()) {
final SettableApiFuture result = SettableApiFuture.create();
- internalStream(
+
+ ApiStreamObserver observer =
new ApiStreamObserver() {
final List documentSnapshots = new ArrayList<>();
Timestamp responseReadTime;
@@ -139,7 +157,10 @@ public void onCompleted() {
SnapshotType querySnapshot = createSnaphot(responseReadTime, resultView);
result.set(querySnapshot);
}
- },
+ };
+
+ internalStream(
+ new MonitoredStreamResponseObserver(observer, metricsContext),
/* startTimeNanos= */ rpcContext.getClock().nanoTime(),
transactionId,
/* readTime= */ requestReadTime,
@@ -150,6 +171,7 @@ public void onCompleted() {
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error);
throw error;
}
}
@@ -165,11 +187,18 @@ public void onCompleted() {
@Nonnull
public ApiFuture> explain(ExplainOptions options) {
TraceUtil.Span span =
- getFirestore().getOptions().getTraceUtil().startSpan(TraceUtil.SPAN_NAME_QUERY_GET);
+ getFirestore()
+ .getOptions()
+ .getTraceUtil()
+ .startSpan(TelemetryConstants.METHOD_NAME_QUERY_GET);
+
+ MetricsContext metricsContext =
+ createMetricsContext(TelemetryConstants.METHOD_NAME_RUN_QUERY_EXPLAIN);
try (Scope ignored = span.makeCurrent()) {
final SettableApiFuture> result = SettableApiFuture.create();
- internalStream(
+
+ ApiStreamObserver observer =
new ApiStreamObserver() {
@Nullable List documentSnapshots = null;
Timestamp readTime;
@@ -222,7 +251,10 @@ public void onCompleted() {
}
result.set(new ExplainResults<>(metrics, snapshot));
}
- },
+ };
+
+ internalStream(
+ new MonitoredStreamResponseObserver(observer, metricsContext),
/* startTimeNanos= */ rpcContext.getClock().nanoTime(),
/* transactionId= */ null,
/* readTime= */ null,
@@ -233,25 +265,61 @@ public void onCompleted() {
return result;
} catch (Exception error) {
span.end(error);
+ metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error);
throw error;
}
}
- protected void internalStream(
- final ApiStreamObserver runQueryResponseObserver,
+ class MonitoredStreamResponseObserver implements ApiStreamObserver {
+ private final ApiStreamObserver observer;
+ private final MetricsContext metricsContext;
+ private boolean receivedFirstResponse = false;
+
+ // Constructor to initialize with the delegate and MetricsContext
+ public MonitoredStreamResponseObserver(
+ ApiStreamObserver observer, MetricsContext metricsContext) {
+ this.observer = observer;
+ this.metricsContext = metricsContext;
+ }
+
+ @Override
+ public void onNext(RunQueryResponse value) {
+ if (!receivedFirstResponse) {
+ receivedFirstResponse = true;
+ metricsContext.recordLatency(MetricType.FIRST_RESPONSE_LATENCY);
+ }
+ observer.onNext(value);
+ }
+
+ @Override
+ public void onError(Throwable t) {
+ metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, t);
+ observer.onError(t);
+ }
+
+ @Override
+ public void onCompleted() {
+ metricsContext.recordLatency(MetricType.END_TO_END_LATENCY);
+ observer.onCompleted();
+ }
+ }
+
+ void internalStream(
+ final MonitoredStreamResponseObserver streamResponseObserver,
final long startTimeNanos,
@Nullable final ByteString transactionId,
@Nullable final Timestamp readTime,
@Nullable final ExplainOptions explainOptions,
final boolean isRetryRequestWithCursor) {
TraceUtil traceUtil = getFirestore().getOptions().getTraceUtil();
+
// To reduce the size of traces, we only register one event for every 100 responses
// that we receive from the server.
final int NUM_RESPONSES_PER_TRACE_EVENT = 100;
TraceUtil.Span currentSpan = traceUtil.currentSpan();
currentSpan.addEvent(
- TraceUtil.SPAN_NAME_RUN_QUERY,
+ TelemetryConstants.METHOD_NAME_RUN_QUERY,
new ImmutableMap.Builder()
.put(ATTRIBUTE_KEY_IS_TRANSACTIONAL, transactionId != null)
.put(ATTRIBUTE_KEY_IS_RETRY_WITH_CURSOR, isRetryRequestWithCursor)
@@ -276,16 +344,19 @@ public void onStart(StreamController streamController) {}
public void onResponse(RunQueryResponse response) {
if (!firstResponse) {
firstResponse = true;
- currentSpan.addEvent(TraceUtil.SPAN_NAME_RUN_QUERY + ": First Response");
+ currentSpan.addEvent(TelemetryConstants.METHOD_NAME_RUN_QUERY + ": First Response");
}
- runQueryResponseObserver.onNext(response);
+ streamResponseObserver.onNext(response);
if (response.hasDocument()) {
numDocuments++;
if (numDocuments % NUM_RESPONSES_PER_TRACE_EVENT == 0) {
currentSpan.addEvent(
- TraceUtil.SPAN_NAME_RUN_QUERY + ": Received " + numDocuments + " documents");
+ TelemetryConstants.METHOD_NAME_RUN_QUERY
+ + ": Received "
+ + numDocuments
+ + " documents");
}
Document document = response.getDocument();
QueryDocumentSnapshot documentSnapshot =
@@ -296,7 +367,7 @@ public void onResponse(RunQueryResponse response) {
if (response.getDone()) {
currentSpan.addEvent(
- TraceUtil.SPAN_NAME_RUN_QUERY + ": Received RunQueryResponse.Done");
+ TelemetryConstants.METHOD_NAME_RUN_QUERY + ": Received RunQueryResponse.Done");
onComplete();
}
}
@@ -306,12 +377,12 @@ public void onError(Throwable throwable) {
QueryDocumentSnapshot cursor = lastReceivedDocument.get();
if (isRetryableWithCursor() && shouldRetry(cursor, throwable)) {
currentSpan.addEvent(
- TraceUtil.SPAN_NAME_RUN_QUERY + ": Retryable Error",
- Collections.singletonMap("error.message", throwable.getMessage()));
+ TelemetryConstants.METHOD_NAME_RUN_QUERY + ": Retryable Error",
+ Collections.singletonMap("error.message", throwable.toString()));
startAfter(cursor)
.internalStream(
- runQueryResponseObserver,
+ streamResponseObserver,
startTimeNanos,
/* transactionId= */ null,
options.getRequireConsistency() ? cursor.getReadTime() : null,
@@ -319,9 +390,9 @@ public void onError(Throwable throwable) {
/* isRetryRequestWithCursor= */ true);
} else {
currentSpan.addEvent(
- TraceUtil.SPAN_NAME_RUN_QUERY + ": Error",
- Collections.singletonMap("error.message", throwable.getMessage()));
- runQueryResponseObserver.onError(throwable);
+ TelemetryConstants.METHOD_NAME_RUN_QUERY + ": Error",
+ Collections.singletonMap("error.message", throwable.toString()));
+ streamResponseObserver.onError(throwable);
}
}
@@ -330,9 +401,9 @@ public void onComplete() {
if (hasCompleted) return;
hasCompleted = true;
currentSpan.addEvent(
- TraceUtil.SPAN_NAME_RUN_QUERY + ": Completed",
+ TelemetryConstants.METHOD_NAME_RUN_QUERY + ": Completed",
Collections.singletonMap(ATTRIBUTE_KEY_DOC_COUNT, numDocuments));
- runQueryResponseObserver.onCompleted();
+ streamResponseObserver.onCompleted();
}
boolean shouldRetry(DocumentSnapshot lastDocument, Throwable t) {
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Transaction.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Transaction.java
index 04d83a1a12..ff1b7e2d67 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Transaction.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/Transaction.java
@@ -18,6 +18,7 @@
import com.google.api.core.ApiFuture;
import com.google.api.core.InternalExtensionOnly;
+import com.google.cloud.firestore.telemetry.MetricsUtil;
import com.google.cloud.firestore.telemetry.TraceUtil;
import com.google.cloud.firestore.telemetry.TraceUtil.Context;
import java.util.List;
@@ -49,6 +50,12 @@ TraceUtil getTraceUtil() {
return firestore.getOptions().getTraceUtil();
}
+ // TODO(Metrics): implement transaction latency and attempt count metrics
+ @Nonnull
+ MetricsUtil getMetricsUtil() {
+ return firestore.getOptions().getMetricsUtil();
+ }
+
@Nonnull
Context setTransactionTraceContext(Context context) {
return transactionTraceContext = context;
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java
index 31434667ba..cfa852ce4c 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/UpdateBuilder.java
@@ -26,6 +26,7 @@
import com.google.api.core.InternalExtensionOnly;
import com.google.cloud.firestore.UserDataConverter.EncodingOptions;
import com.google.cloud.firestore.encoding.CustomClassMapper;
+import com.google.cloud.firestore.telemetry.TelemetryConstants;
import com.google.cloud.firestore.telemetry.TraceUtil;
import com.google.cloud.firestore.telemetry.TraceUtil.Scope;
import com.google.common.base.Preconditions;
@@ -616,8 +617,8 @@ ApiFuture> commit(@Nullable ByteString transactionId) {
.getTraceUtil()
.startSpan(
transactionId == null
- ? TraceUtil.SPAN_NAME_BATCH_COMMIT
- : TraceUtil.SPAN_NAME_TRANSACTION_COMMIT);
+ ? TelemetryConstants.METHOD_NAME_BATCH_COMMIT
+ : TelemetryConstants.METHOD_NAME_TRANSACTION_COMMIT);
span.setAttribute(ATTRIBUTE_KEY_DOC_COUNT, writes.size());
span.setAttribute(ATTRIBUTE_KEY_IS_TRANSACTIONAL, transactionId != null);
try (Scope ignored = span.makeCurrent()) {
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 c391e9b801..9668fbeb09 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,6 +28,7 @@
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.ApiTracerFactory;
import com.google.cloud.NoCredentials;
import com.google.cloud.ServiceOptions;
import com.google.cloud.firestore.FirestoreOptions;
@@ -157,6 +158,11 @@ public GrpcFirestoreRpc(final FirestoreOptions options) throws IOException {
firestoreBuilder.batchGetDocumentsSettings().setRetrySettings(retrySettings);
}
+ ApiTracerFactory apiTracerFactory = options.getApiTracerFactory();
+ if (apiTracerFactory != null) {
+ firestoreBuilder.setTracerFactory(apiTracerFactory);
+ }
+
firestoreStub = GrpcFirestoreStub.create(firestoreBuilder.build());
} catch (Exception e) {
throw new IOException(e);
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java
new file mode 100644
index 0000000000..621efb914c
--- /dev/null
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/BuiltinMetricsProvider.java
@@ -0,0 +1,181 @@
+/*
+ * 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 static com.google.cloud.firestore.telemetry.TelemetryConstants.FIRESTORE_METER_NAME;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_ATTRIBUTE_KEY_CLIENT_UID;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_ATTRIBUTE_KEY_LIBRARY_NAME;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_ATTRIBUTE_KEY_LIBRARY_VERSION;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_NAME_END_TO_END_LATENCY;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_NAME_FIRST_RESPONSE_LATENCY;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_NAME_TRANSACTION_ATTEMPT_COUNT;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_NAME_TRANSACTION_LATENCY;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_PREFIX;
+
+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.telemetry.TelemetryConstants.MetricType;
+import io.opentelemetry.api.OpenTelemetry;
+import io.opentelemetry.api.common.Attributes;
+import io.opentelemetry.api.common.AttributesBuilder;
+import io.opentelemetry.api.metrics.DoubleHistogram;
+import io.opentelemetry.api.metrics.LongCounter;
+import io.opentelemetry.api.metrics.Meter;
+import io.opentelemetry.api.metrics.MeterProvider;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+/**
+ * A provider for built-in metrics. This class is responsible for storing OpenTelemetry metrics
+ * configuration and recording built-in metrics for the Firestore SDK.
+ */
+class BuiltinMetricsProvider {
+ private static final Logger logger = Logger.getLogger(BuiltinMetricsProvider.class.getName());
+
+ private OpenTelemetry openTelemetry;
+ private DoubleHistogram endToEndLatency;
+ private DoubleHistogram firstResponseLatency;
+ private DoubleHistogram transactionLatency;
+ private LongCounter transactionAttemptCount;
+
+ private ApiTracerFactory apiTracerFactory;
+ private final Map staticAttributes;
+
+ private static final String MILLISECOND_UNIT = "ms";
+ private static final String INTEGER_UNIT = "1";
+ private static final String FIRESTORE_LIBRARY_NAME = "com.google.cloud.firestore";
+
+ public BuiltinMetricsProvider(OpenTelemetry openTelemetry) {
+ this.openTelemetry = openTelemetry;
+ this.staticAttributes = createStaticAttributes();
+
+ if (openTelemetry.getMeterProvider() != MeterProvider.noop()) {
+ configureRPCLayerMetrics();
+ configureSDKLayerMetrics();
+ }
+ }
+
+ private Map createStaticAttributes() {
+ Map staticAttributes = new HashMap<>();
+ staticAttributes.put(METRIC_ATTRIBUTE_KEY_CLIENT_UID.getKey(), ClientIdentifier.getClientUid());
+ staticAttributes.put(METRIC_ATTRIBUTE_KEY_LIBRARY_NAME.getKey(), FIRESTORE_LIBRARY_NAME);
+ String pkgVersion = this.getClass().getPackage().getImplementationVersion();
+ if (pkgVersion != null) {
+ staticAttributes.put(METRIC_ATTRIBUTE_KEY_LIBRARY_VERSION.getKey(), pkgVersion);
+ }
+ return staticAttributes;
+ }
+
+ /** Creates an ApiTracerFactory to be passed into GAX library and collect RPC layer metrics. */
+ private void configureRPCLayerMetrics() {
+ OpenTelemetryMetricsRecorder recorder =
+ new OpenTelemetryMetricsRecorder(openTelemetry, METRIC_PREFIX);
+ this.apiTracerFactory = new MetricsTracerFactory(recorder, staticAttributes);
+ }
+
+ /** Registers metrics to be collected at the Firestore SDK layer */
+ private void configureSDKLayerMetrics() {
+ Meter meter = openTelemetry.getMeter(FIRESTORE_METER_NAME);
+
+ this.endToEndLatency =
+ meter
+ .histogramBuilder(METRIC_PREFIX + "/" + METRIC_NAME_END_TO_END_LATENCY)
+ .setDescription("Firestore operations' end-to-end latency")
+ .setUnit(MILLISECOND_UNIT)
+ .build();
+
+ this.firstResponseLatency =
+ meter
+ .histogramBuilder(METRIC_PREFIX + "/" + METRIC_NAME_FIRST_RESPONSE_LATENCY)
+ .setDescription("Firestore streaming operations' first response latency")
+ .setUnit(MILLISECOND_UNIT)
+ .build();
+
+ this.transactionLatency =
+ meter
+ .histogramBuilder(METRIC_PREFIX + "/" + METRIC_NAME_TRANSACTION_LATENCY)
+ .setDescription("Firestore transactions' end-to-end latency")
+ .setUnit(MILLISECOND_UNIT)
+ .build();
+
+ this.transactionAttemptCount =
+ meter
+ .counterBuilder(METRIC_PREFIX + "/" + METRIC_NAME_TRANSACTION_ATTEMPT_COUNT)
+ .setDescription("Number of Firestore transaction attempts including retries")
+ .setUnit(INTEGER_UNIT)
+ .build();
+ }
+
+ public ApiTracerFactory getApiTracerFactory() {
+ return this.apiTracerFactory;
+ }
+
+ public void latencyRecorder(
+ MetricType metricType, double latency, Map attributes) {
+ DoubleHistogram histogram = getHistogram(metricType);
+ if (histogram != null) {
+ attributes.putAll(staticAttributes);
+ try {
+ histogram.record(latency, toOtelAttributes(attributes));
+ } catch (Exception e) {
+ logger.log(Level.WARNING, "Failed to record latency metric: " + e.getMessage(), e);
+ }
+ }
+ }
+
+ public void counterRecorder(MetricType metricType, long count, Map attributes) {
+ LongCounter counter = getCounter(metricType);
+ if (counter != null) {
+ attributes.putAll(staticAttributes);
+ try {
+ counter.add(count, toOtelAttributes(attributes));
+ } catch (Exception e) {
+ logger.log(Level.WARNING, "Failed to record counter metric:" + e.getMessage(), e);
+ }
+ }
+ }
+
+ public DoubleHistogram getHistogram(MetricType metricType) {
+ switch (metricType) {
+ case END_TO_END_LATENCY:
+ return endToEndLatency;
+ case FIRST_RESPONSE_LATENCY:
+ return firstResponseLatency;
+ case TRANSACTION_LATENCY:
+ return transactionLatency;
+ default:
+ throw new IllegalArgumentException("Unknown latency MetricType: " + metricType);
+ }
+ }
+
+ public LongCounter getCounter(MetricType metricType) {
+ if (metricType == MetricType.TRANSACTION_ATTEMPT_COUNT) {
+ return transactionAttemptCount;
+ } else {
+ throw new IllegalArgumentException("Unknown counter MetricType: " + metricType);
+ }
+ }
+
+ private Attributes toOtelAttributes(Map attributes) {
+ AttributesBuilder attributesBuilder = Attributes.builder();
+ attributes.forEach(attributesBuilder::put);
+ return attributesBuilder.build();
+ }
+}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/ClientIdentifier.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/ClientIdentifier.java
new file mode 100644
index 0000000000..dc4a3eae05
--- /dev/null
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/ClientIdentifier.java
@@ -0,0 +1,77 @@
+/*
+ * 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 java.lang.management.ManagementFactory;
+import java.lang.reflect.Method;
+import java.net.InetAddress;
+import java.util.UUID;
+
+/** A utility class for retrieving a unique client identifier (CLIENT_UID) */
+final class ClientIdentifier {
+
+ private ClientIdentifier() {}
+
+ private static String CLIENT_UID;
+
+ /** Gets the unique identifier for the client. */
+ public static String getClientUid() {
+ if (CLIENT_UID == null) {
+ CLIENT_UID = generateClientUid();
+ }
+ return CLIENT_UID;
+ }
+
+ /**
+ * Generates a unique identifier for the client that is composed of a random UUID, the process ID,
+ * and the hostname of the machine.
+ */
+ private static String generateClientUid() {
+ String identifier = UUID.randomUUID().toString();
+ String pid = getProcessId();
+ String hostname = getHostName();
+ return String.format("%s@%s@%s", identifier, pid, hostname);
+ }
+
+ private static String getHostName() {
+ try {
+ return InetAddress.getLocalHost().getHostName();
+ } catch (Exception e) {
+ return "localhost";
+ }
+ }
+
+ private static String getProcessId() {
+ try {
+ // Check if Java 9+ and ProcessHandle class is available
+ Class> processHandleClass = Class.forName("java.lang.ProcessHandle");
+ Method currentMethod = processHandleClass.getMethod("current");
+ Object processHandleInstance = currentMethod.invoke(null);
+ Method pidMethod = processHandleClass.getMethod("pid");
+ long pid = (long) pidMethod.invoke(processHandleInstance);
+ return Long.toString(pid);
+ } catch (Exception e) {
+ // Fallback to Java 8 method
+ final String jvmName = ManagementFactory.getRuntimeMXBean().getName();
+ if (jvmName != null && jvmName.contains("@")) {
+ return jvmName.split("@")[0];
+ } else {
+ return "unknown";
+ }
+ }
+ }
+}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/CompositeApiTracer.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/CompositeApiTracer.java
new file mode 100644
index 0000000000..31dbddbbf3
--- /dev/null
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/CompositeApiTracer.java
@@ -0,0 +1,129 @@
+/*
+ * 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.ApiTracer;
+import com.google.api.gax.tracing.BaseApiTracer;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+import org.threeten.bp.Duration;
+
+/** Combines multiple {@link ApiTracer}s into a single {@link ApiTracer}. */
+class CompositeApiTracer extends BaseApiTracer {
+ private final List children;
+
+ public CompositeApiTracer(List children) {
+ this.children = ImmutableList.copyOf(children);
+ }
+
+ @Override
+ public Scope inScope() {
+ List childScopes =
+ children.stream()
+ .map(ApiTracer::inScope)
+ .collect(Collectors.toCollection(() -> new ArrayList<>(children.size())));
+
+ return () -> childScopes.forEach(Scope::close);
+ }
+
+ @Override
+ public void operationSucceeded() {
+ children.forEach(ApiTracer::operationSucceeded);
+ }
+
+ @Override
+ public void operationCancelled() {
+ children.forEach(ApiTracer::operationCancelled);
+ }
+
+ @Override
+ public void operationFailed(Throwable error) {
+ children.forEach(child -> child.operationFailed(error));
+ }
+
+ @Override
+ public void connectionSelected(String id) {
+ children.forEach(child -> child.connectionSelected(id));
+ }
+
+ @Override
+ public void attemptStarted(int attemptNumber) {
+ children.forEach(child -> child.attemptStarted(null, attemptNumber));
+ }
+
+ @Override
+ public void attemptStarted(Object request, int attemptNumber) {
+ children.forEach(child -> child.attemptStarted(request, attemptNumber));
+ }
+
+ @Override
+ public void attemptSucceeded() {
+ children.forEach(ApiTracer::attemptSucceeded);
+ }
+
+ @Override
+ public void attemptCancelled() {
+ children.forEach(ApiTracer::attemptCancelled);
+ }
+
+ @Override
+ public void attemptFailed(Throwable error, Duration delay) {
+ children.forEach(child -> child.attemptFailed(error, delay));
+ }
+
+ @Override
+ public void attemptFailedDuration(Throwable error, java.time.Duration delay) {
+ children.forEach(child -> child.attemptFailedDuration(error, delay));
+ }
+
+ @Override
+ public void attemptFailedRetriesExhausted(Throwable error) {
+ children.forEach(child -> child.attemptFailedRetriesExhausted(error));
+ }
+
+ @Override
+ public void attemptPermanentFailure(Throwable error) {
+ children.forEach(child -> child.attemptPermanentFailure(error));
+ }
+
+ @Override
+ public void lroStartFailed(Throwable error) {
+ children.forEach(child -> child.lroStartFailed(error));
+ }
+
+ @Override
+ public void lroStartSucceeded() {
+ children.forEach(ApiTracer::lroStartSucceeded);
+ }
+
+ @Override
+ public void responseReceived() {
+ children.forEach(ApiTracer::responseReceived);
+ }
+
+ @Override
+ public void requestSent() {
+ children.forEach(ApiTracer::requestSent);
+ }
+
+ @Override
+ public void batchRequestSent(long elementCount, long requestSize) {
+ children.forEach(child -> child.batchRequestSent(elementCount, requestSize));
+ }
+}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/CompositeApiTracerFactory.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/CompositeApiTracerFactory.java
new file mode 100644
index 0000000000..ddd0f226d1
--- /dev/null
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/CompositeApiTracerFactory.java
@@ -0,0 +1,47 @@
+/*
+ * 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.core.InternalApi;
+import com.google.api.gax.tracing.ApiTracer;
+import com.google.api.gax.tracing.ApiTracerFactory;
+import com.google.api.gax.tracing.BaseApiTracerFactory;
+import com.google.api.gax.tracing.SpanName;
+import com.google.common.collect.ImmutableList;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Combines multiple {@link ApiTracerFactory} into a single {@link ApiTracerFactory}. */
+@InternalApi
+public class CompositeApiTracerFactory extends BaseApiTracerFactory {
+
+ private final List apiTracerFactories;
+
+ public CompositeApiTracerFactory(List apiTracerFactories) {
+ this.apiTracerFactories = ImmutableList.copyOf(apiTracerFactories);
+ }
+
+ @Override
+ public ApiTracer newTracer(ApiTracer parent, SpanName spanName, OperationType operationType) {
+ List children = new ArrayList<>(apiTracerFactories.size());
+
+ for (ApiTracerFactory factory : apiTracerFactories) {
+ children.add(factory.newTracer(parent, spanName, operationType));
+ }
+ return new CompositeApiTracer(children);
+ }
+}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java
new file mode 100644
index 0000000000..0642033c00
--- /dev/null
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledMetricsUtil.java
@@ -0,0 +1,55 @@
+/*
+ * 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.core.ApiFuture;
+import com.google.api.gax.tracing.ApiTracerFactory;
+import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType;
+import java.util.List;
+
+/**
+ * A fully disabled (No-op) MetricsUtil class that does not perform any metrics collection actions
+ * and has near-zero overhead.
+ */
+class DisabledMetricsUtil implements MetricsUtil {
+
+ class MetricsContext implements MetricsUtil.MetricsContext {
+
+ @Override
+ public void recordLatencyAtFuture(MetricType metric, ApiFuture futureValue) {}
+
+ @Override
+ public void recordLatency(MetricType metric) {}
+
+ @Override
+ public void recordLatency(MetricType metric, Throwable t) {}
+
+ @Override
+ public void recordCounterAtFuture(MetricType metric, ApiFuture futureValue) {}
+
+ @Override
+ public void incrementCounter() {}
+ }
+
+ @Override
+ public MetricsContext createMetricsContext(String methodName) {
+ return new MetricsContext();
+ }
+
+ @Override
+ public void addMetricsTracerFactory(List apiTracerFactories) {}
+}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledTraceUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledTraceUtil.java
index c2b47d536f..16428b8cfd 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledTraceUtil.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/DisabledTraceUtil.java
@@ -18,15 +18,17 @@
import com.google.api.core.ApiFunction;
import com.google.api.core.ApiFuture;
+import com.google.api.core.InternalApi;
import io.grpc.ManagedChannelBuilder;
import java.util.Map;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
- * A fully disabled (No-op) tracing utility class that does not perform any tracing actions and has
+ * A no-op implementation of {@link MetricsUtil} that does not collect or export any metrics and has
* near-zero overhead.
*/
+@InternalApi
public class DisabledTraceUtil implements TraceUtil {
static class Span implements TraceUtil.Span {
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java
new file mode 100644
index 0000000000..5a9bd4d7b6
--- /dev/null
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledMetricsUtil.java
@@ -0,0 +1,262 @@
+/*
+ * 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 static com.google.cloud.firestore.telemetry.TelemetryConstants.COMMON_ATTRIBUTES;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.FIRESTORE_METER_NAME;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.FIRESTORE_METRICS;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.GAX_METER_NAME;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.GAX_METRICS;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_ATTRIBUTE_KEY_METHOD;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_ATTRIBUTE_KEY_STATUS;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.METRIC_PREFIX;
+
+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.cloud.firestore.FirestoreException;
+import com.google.cloud.firestore.FirestoreOptions;
+import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType;
+import com.google.cloud.opentelemetry.metric.GoogleCloudMetricExporter;
+import com.google.cloud.opentelemetry.metric.MetricConfiguration;
+import com.google.common.base.Stopwatch;
+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.GlobalOpenTelemetry;
+import io.opentelemetry.api.OpenTelemetry;
+import io.opentelemetry.api.common.AttributeKey;
+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 java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.concurrent.TimeUnit;
+import java.util.logging.Logger;
+import java.util.stream.Collectors;
+import javax.annotation.Nullable;
+
+/**
+ * An implementation of {@link MetricsUtil} that uses OpenTelemetry to collect and export metrics.
+ * `FirestoreOpenTelemetryOptions` in `FirestoreOptions` can be used to configure its behavior.
+ */
+class EnabledMetricsUtil implements MetricsUtil {
+ private BuiltinMetricsProvider defaultMetricsProvider;
+ private BuiltinMetricsProvider customMetricsProvider;
+
+ private static final Logger logger = Logger.getLogger(EnabledMetricsUtil.class.getName());
+
+ EnabledMetricsUtil(FirestoreOptions firestoreOptions) {
+ try {
+ configureDefaultMetricsProvider(firestoreOptions);
+ configureCustomMetricsProvider(firestoreOptions);
+ } catch (IOException e) {
+ logger.warning(
+ "Unable to create MetricsUtil object for client side metrics, will skip exporting client side metrics"
+ + e);
+ }
+ }
+
+ private void configureDefaultMetricsProvider(FirestoreOptions firestoreOptions)
+ throws IOException {
+ OpenTelemetry defaultOpenTelemetry;
+ boolean exportBuiltinMetricsToGoogleCloudMonitoring =
+ firestoreOptions.getOpenTelemetryOptions().exportBuiltinMetricsToGoogleCloudMonitoring();
+ if (exportBuiltinMetricsToGoogleCloudMonitoring) {
+ defaultOpenTelemetry = getDefaultOpenTelemetryInstance(firestoreOptions.getProjectId());
+ } else {
+ defaultOpenTelemetry = OpenTelemetry.noop();
+ }
+ this.defaultMetricsProvider = new BuiltinMetricsProvider(defaultOpenTelemetry);
+ }
+
+ private void configureCustomMetricsProvider(FirestoreOptions firestoreOptions)
+ throws IOException {
+ OpenTelemetry customOpenTelemetry =
+ firestoreOptions.getOpenTelemetryOptions().getOpenTelemetry();
+ if (customOpenTelemetry == null) {
+ customOpenTelemetry = GlobalOpenTelemetry.get();
+ }
+ this.customMetricsProvider = new BuiltinMetricsProvider(customOpenTelemetry);
+ }
+
+ @Override
+ public MetricsContext createMetricsContext(String methodName) {
+ return new MetricsContext(methodName);
+ }
+
+ @Override
+ public void addMetricsTracerFactory(List apiTracerFactories) {
+ addTracerFactory(apiTracerFactories, defaultMetricsProvider);
+ addTracerFactory(apiTracerFactories, customMetricsProvider);
+ }
+
+ /**
+ * Creates a default {@link OpenTelemetry} instance to collect and export built-in client side
+ * metrics to Google Cloud Monitoring.
+ */
+ private OpenTelemetry getDefaultOpenTelemetryInstance(String projectId) throws IOException {
+ SdkMeterProviderBuilder sdkMeterProviderBuilder = SdkMeterProvider.builder();
+
+ // Filter out attributes that are not defined
+ for (Map.Entry entry : getAllViews().entrySet()) {
+ sdkMeterProviderBuilder.registerView(entry.getKey(), entry.getValue());
+ }
+
+ MetricExporter metricExporter =
+ GoogleCloudMetricExporter.createWithConfiguration(
+ MetricConfiguration.builder()
+ .setProjectId(projectId)
+ // Ignore library info as it is collected by the metric attributes as well
+ .setInstrumentationLibraryLabelsEnabled(false)
+ .build());
+ sdkMeterProviderBuilder.registerMetricReader(PeriodicMetricReader.create(metricExporter));
+
+ return OpenTelemetrySdk.builder().setMeterProvider(sdkMeterProviderBuilder.build()).build();
+ }
+
+ private static Map getAllViews() {
+ ImmutableMap.Builder views = ImmutableMap.builder();
+ GAX_METRICS.forEach(metric -> defineView(views, metric, GAX_METER_NAME));
+ FIRESTORE_METRICS.forEach(metric -> defineView(views, metric, FIRESTORE_METER_NAME));
+ return views.build();
+ }
+
+ private static void defineView(
+ ImmutableMap.Builder viewMap, String id, String meter) {
+ InstrumentSelector selector =
+ InstrumentSelector.builder().setMeterName(meter).setName(METRIC_PREFIX + "/" + id).build();
+ Set attributesFilter =
+ ImmutableSet.builder()
+ .addAll(
+ COMMON_ATTRIBUTES.stream().map(AttributeKey::getKey).collect(Collectors.toSet()))
+ .build();
+ View view = View.builder().setAttributeFilter(attributesFilter).build();
+
+ viewMap.put(selector, view);
+ }
+
+ private void addTracerFactory(
+ List apiTracerFactories, BuiltinMetricsProvider metricsProvider) {
+ ApiTracerFactory tracerFactory = metricsProvider.getApiTracerFactory();
+ if (tracerFactory != null) {
+ apiTracerFactories.add(tracerFactory);
+ }
+ }
+
+ class MetricsContext implements MetricsUtil.MetricsContext {
+ private final Stopwatch stopwatch;
+ private int counter;
+ protected final String methodName;
+
+ public MetricsContext(String methodName) {
+ this.stopwatch = Stopwatch.createStarted();
+ this.methodName = methodName;
+ this.counter = 0;
+ }
+
+ public void recordLatencyAtFuture(MetricType metric, ApiFuture futureValue) {
+ ApiFutures.addCallback(
+ futureValue,
+ new ApiFutureCallback() {
+ @Override
+ public void onFailure(Throwable t) {
+ recordLatency(metric, t);
+ }
+
+ @Override
+ public void onSuccess(T result) {
+ recordLatency(metric);
+ }
+ },
+ MoreExecutors.directExecutor());
+ }
+
+ public void recordLatency(MetricType metric) {
+ recordLatency(metric, StatusCode.Code.OK.toString());
+ }
+
+ public void recordLatency(MetricType metric, Throwable t) {
+ recordLatency(metric, extractErrorStatus(t));
+ }
+
+ private void recordLatency(MetricType metric, String status) {
+ double elapsedTime = stopwatch.elapsed(TimeUnit.MILLISECONDS);
+ Map attributes = createAttributes(status, methodName);
+ defaultMetricsProvider.latencyRecorder(metric, elapsedTime, attributes);
+ customMetricsProvider.latencyRecorder(metric, elapsedTime, attributes);
+ }
+
+ public void incrementCounter() {
+ counter++;
+ }
+
+ public void recordCounterAtFuture(MetricType metric, ApiFuture futureValue) {
+ ApiFutures.addCallback(
+ futureValue,
+ new ApiFutureCallback() {
+ @Override
+ public void onFailure(Throwable t) {
+ recordCounter(metric, extractErrorStatus(t));
+ }
+
+ @Override
+ public void onSuccess(T result) {
+ recordCounter(metric, StatusCode.Code.OK.toString());
+ }
+ },
+ MoreExecutors.directExecutor());
+ }
+
+ private void recordCounter(MetricType metric, String status) {
+ Map attributes = createAttributes(status, methodName);
+ defaultMetricsProvider.counterRecorder(
+ MetricType.TRANSACTION_ATTEMPT_COUNT, (long) counter, attributes);
+ customMetricsProvider.counterRecorder(
+ MetricType.TRANSACTION_ATTEMPT_COUNT, (long) counter, attributes);
+ }
+ }
+
+ private Map createAttributes(String status, String methodName) {
+ Map attributes = new HashMap<>();
+ attributes.put(METRIC_ATTRIBUTE_KEY_METHOD.getKey(), methodName);
+ attributes.put(METRIC_ATTRIBUTE_KEY_STATUS.getKey(), status);
+ return attributes;
+ }
+
+ private String extractErrorStatus(@Nullable Throwable throwable) {
+ if (!(throwable instanceof FirestoreException)) {
+ return StatusCode.Code.UNKNOWN.toString();
+ }
+
+ Status status = ((FirestoreException) throwable).getStatus();
+ if (status == null) {
+ return StatusCode.Code.UNKNOWN.toString();
+ }
+ return status.getCode().name();
+ }
+}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledTraceUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledTraceUtil.java
index 52bb7c5e08..b0eeeca218 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledTraceUtil.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/EnabledTraceUtil.java
@@ -20,6 +20,7 @@
import com.google.api.core.ApiFuture;
import com.google.api.core.ApiFutureCallback;
import com.google.api.core.ApiFutures;
+import com.google.api.core.InternalApi;
import com.google.cloud.firestore.FirestoreOptions;
import com.google.common.base.Throwables;
import io.grpc.ManagedChannelBuilder;
@@ -31,6 +32,7 @@
import io.opentelemetry.api.trace.SpanKind;
import io.opentelemetry.api.trace.StatusCode;
import io.opentelemetry.api.trace.Tracer;
+import io.opentelemetry.api.trace.TracerProvider;
import io.opentelemetry.instrumentation.grpc.v1_6.GrpcTelemetry;
import java.util.Map;
import javax.annotation.Nonnull;
@@ -40,6 +42,7 @@
* A utility class that uses OpenTelemetry for trace collection. `FirestoreOpenTelemetryOptions` in
* `FirestoreOptions` can be used to configure its behavior.
*/
+@InternalApi
public class EnabledTraceUtil implements TraceUtil {
private final Tracer tracer;
private final OpenTelemetry openTelemetry;
@@ -48,8 +51,7 @@ public class EnabledTraceUtil implements TraceUtil {
EnabledTraceUtil(FirestoreOptions firestoreOptions) {
OpenTelemetry openTelemetry = firestoreOptions.getOpenTelemetryOptions().getOpenTelemetry();
- // If tracing is enabled, but an OpenTelemetry instance is not provided, fall back
- // to using GlobalOpenTelemetry.
+ // If an OpenTelemetry instance is not provided, fall back to using GlobalOpenTelemetry.
if (openTelemetry == null) {
openTelemetry = GlobalOpenTelemetry.get();
}
@@ -84,6 +86,11 @@ public ManagedChannelBuilder apply(ManagedChannelBuilder managedChannelBuilder)
@Override
@Nullable
public ApiFunction getChannelConfigurator() {
+ // Note: using `==` rather than `.equals` since OpenTelemetry has only 1 static instance of
+ // `TracerProvider.noop`.
+ if (openTelemetry.getTracerProvider() == TracerProvider.noop()) {
+ return null;
+ }
return new OpenTelemetryGrpcChannelConfigurator();
}
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
new file mode 100644
index 0000000000..02ac3a26be
--- /dev/null
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java
@@ -0,0 +1,119 @@
+/*
+ * 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.core.ApiFuture;
+import com.google.api.core.InternalApi;
+import com.google.api.gax.tracing.ApiTracerFactory;
+import com.google.cloud.firestore.FirestoreOptions;
+import com.google.cloud.firestore.telemetry.TelemetryConstants.MetricType;
+import java.util.List;
+import java.util.logging.Logger;
+import javax.annotation.Nonnull;
+
+/**
+ * A utility interface for metrics collection. Classes that implement this interface may make their
+ * own design choices for how they approach metrics collection. For instance, they may be no-op, or
+ * they may use a particular metrics framework such as OpenTelemetry.
+ */
+@InternalApi
+public interface MetricsUtil {
+ final Logger logger = Logger.getLogger(MetricsUtil.class.getName());
+
+ static final String ENABLE_METRICS_ENV_VAR = "FIRESTORE_ENABLE_METRICS";
+
+ /**
+ * Creates an instance of {@code MetricsUtil}. If the environment variable
+ * `FIRESTORE_ENABLE_METRICS` is set to false or off, an instance of {@link DisabledMetricsUtil}
+ * will be returned. Otherwise, an instance of {@link EnabledMetricsUtil} will be returned.
+ *
+ * @param firestoreOptions The Firestore options that configures client side metrics.
+ * @return An instance of {@code MetricsUtil}.
+ */
+ static MetricsUtil getInstance(@Nonnull FirestoreOptions firestoreOptions) {
+ if (shouldCreateEnabledInstance()) {
+ return new EnabledMetricsUtil(firestoreOptions);
+ } else {
+ return new DisabledMetricsUtil();
+ }
+ }
+
+ static boolean shouldCreateEnabledInstance() {
+ // Client side metrics feature is default on unless it is manually turned off by
+ // environment variables
+ // TODO(metrics): The feature is disabled before it is ready for general release.
+ boolean shouldCreateEnabledInstance = false;
+
+ String enableMetricsEnvVar = System.getenv(ENABLE_METRICS_ENV_VAR);
+ if (enableMetricsEnvVar != null) {
+ switch (enableMetricsEnvVar.toLowerCase()) {
+ case "true":
+ case "on":
+ // The feature is default on.
+ break;
+ case "false":
+ case "off":
+ shouldCreateEnabledInstance = false;
+ break;
+ default:
+ logger.warning("Invalid value for FIRESTORE_ENABLE_METRICS: " + enableMetricsEnvVar);
+ }
+ }
+
+ return shouldCreateEnabledInstance;
+ }
+
+ /**
+ * Creates a new {@code MetricsContext} for the given method and starts timing.
+ *
+ * @param methodName The name of the method.
+ * @return A new {@code MetricsContext}.
+ */
+ abstract MetricsContext createMetricsContext(String methodName);
+
+ /**
+ * Adds a metrics tracer factory to the given list of API tracer factories.
+ *
+ * @param apiTracerFactories The list of API tracer factories.
+ */
+ abstract void addMetricsTracerFactory(List apiTracerFactories);
+
+ /** A context for recording metrics in the Firestore SDK. */
+ interface MetricsContext {
+ /**
+ * If the operation ends in the future, its relevant metrics should be recorded _after_ the
+ * future has been completed. This method "appends" the metrics recording code at the completion
+ * of the given future.
+ */
+ void recordLatencyAtFuture(MetricType metric, ApiFuture futureValue);
+
+ /** Records specific type of latency for the current operation. */
+ void recordLatency(MetricType metric);
+
+ /** Records specific type of latency for the current operation, which ended with a throwable. */
+ void recordLatency(MetricType metric, Throwable t);
+
+ /**
+ * Records the counter value for a metric type _after_ the future has been completed. This
+ * method "appends" the metrics recording code at the completion of the given future.
+ */
+ void recordCounterAtFuture(MetricType metric, ApiFuture futureValue);
+
+ /** Increments the counter tracked inside the MetricsContext. */
+ void incrementCounter();
+ }
+}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java
new file mode 100644
index 0000000000..d2ab8bf173
--- /dev/null
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TelemetryConstants.java
@@ -0,0 +1,124 @@
+/*
+ * 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.core.InternalApi;
+import com.google.api.gax.tracing.OpenTelemetryMetricsRecorder;
+import com.google.common.collect.ImmutableSet;
+import io.opentelemetry.api.common.AttributeKey;
+import java.util.Set;
+
+/** Constants used for telemetry in the Firestore SDK. */
+@InternalApi
+public interface TelemetryConstants {
+ // Method names for Firestore operations
+ String METHOD_NAME_DOC_REF_CREATE = "DocumentReference.Create";
+ String METHOD_NAME_DOC_REF_SET = "DocumentReference.Set";
+ String METHOD_NAME_DOC_REF_UPDATE = "DocumentReference.Update";
+ String METHOD_NAME_DOC_REF_DELETE = "DocumentReference.Delete";
+ String METHOD_NAME_DOC_REF_GET = "DocumentReference.Get";
+ String METHOD_NAME_DOC_REF_LIST_COLLECTIONS = "DocumentReference.ListCollections";
+ String METHOD_NAME_COL_REF_ADD = "CollectionReference.Add";
+ String METHOD_NAME_COL_REF_LIST_DOCUMENTS = "CollectionReference.ListDocuments";
+ String METHOD_NAME_QUERY_GET = "Query.Get";
+ String METHOD_NAME_AGGREGATION_QUERY_GET = "AggregationQuery.Get";
+ String METHOD_NAME_RUN_QUERY = "RunQuery";
+ String METHOD_NAME_RUN_QUERY_EXPLAIN = "RunQuery.Explain";
+ String METHOD_NAME_RUN_QUERY_GET = "RunQuery.Get";
+ String METHOD_NAME_RUN_QUERY_TRANSACTIONAL = "RunQuery.Transactional";
+ String METHOD_NAME_RUN_AGGREGATION_QUERY = "RunAggregationQuery";
+ String METHOD_NAME_RUN_AGGREGATION_QUERY_EXPLAIN = "RunAggregationQuery.Explain";
+ String METHOD_NAME_RUN_AGGREGATION_QUERY_GET = "RunAggregationQuery.Get";
+ String METHOD_NAME_RUN_AGGREGATION_QUERY_TRANSACTIONAL = "RunAggregationQuery.Transactional";
+ String METHOD_NAME_BATCH_GET_DOCUMENTS = "BatchGetDocuments";
+ String METHOD_NAME_BATCH_GET_DOCUMENTS_GET_ALL = "BatchGetDocuments.GetAll";
+ String METHOD_NAME_BATCH_GET_DOCUMENTS_TRANSACTIONAL = "BatchGetDocuments.Transactional";
+ String METHOD_NAME_TRANSACTION_RUN = "Transaction.Run";
+ String METHOD_NAME_TRANSACTION_BEGIN = "Transaction.Begin";
+ String METHOD_NAME_TRANSACTION_GET_QUERY = "Transaction.Get.Query";
+ String METHOD_NAME_TRANSACTION_GET_AGGREGATION_QUERY = "Transaction.Get.AggregationQuery";
+ String METHOD_NAME_TRANSACTION_GET_DOCUMENT = "Transaction.Get.Document";
+ String METHOD_NAME_TRANSACTION_GET_DOCUMENTS = "Transaction.Get.Documents";
+ String METHOD_NAME_TRANSACTION_ROLLBACK = "Transaction.Rollback";
+ String METHOD_NAME_BATCH_COMMIT = "Batch.Commit";
+ String METHOD_NAME_TRANSACTION_COMMIT = "Transaction.Commit";
+ String METHOD_NAME_PARTITION_QUERY = "PartitionQuery";
+ String METHOD_NAME_BULK_WRITER_COMMIT = "BulkWriter.Commit";
+ String METHOD_NAME_RUN_TRANSACTION = "RunTransaction";
+
+ // OpenTelemetry built-in metrics constants
+ String FIRESTORE_RESOURCE_TYPE = "firestore_client_raw";
+ // TODO(metrics): change to firestore.googleapis.com
+ String METRIC_PREFIX = "custom.googleapis.com/internal/client";
+ String FIRESTORE_METER_NAME = "java_firestore";
+ String GAX_METER_NAME = OpenTelemetryMetricsRecorder.GAX_METER_NAME;
+
+ // Monitored resource keys for labels
+ String RESOURCE_KEY_RESOURCE_CONTAINER = "resource_container";
+ String RESOURCE_KEY_LOCATION = "location";
+ String RESOURCE_KEY_DATABASE_ID = "database_id";
+ Set FIRESTORE_RESOURCE_LABELS =
+ ImmutableSet.of(
+ RESOURCE_KEY_RESOURCE_CONTAINER, RESOURCE_KEY_LOCATION, RESOURCE_KEY_DATABASE_ID);
+
+ // Metric attribute keys for labels
+ AttributeKey METRIC_ATTRIBUTE_KEY_METHOD = AttributeKey.stringKey("method");
+ AttributeKey METRIC_ATTRIBUTE_KEY_STATUS = AttributeKey.stringKey("status");
+ AttributeKey METRIC_ATTRIBUTE_KEY_LIBRARY_NAME = AttributeKey.stringKey("library_name");
+ AttributeKey METRIC_ATTRIBUTE_KEY_LIBRARY_VERSION =
+ AttributeKey.stringKey("library_version");
+ AttributeKey METRIC_ATTRIBUTE_KEY_CLIENT_UID = AttributeKey.stringKey("client_uid");
+ Set COMMON_ATTRIBUTES =
+ ImmutableSet.of(
+ METRIC_ATTRIBUTE_KEY_CLIENT_UID,
+ METRIC_ATTRIBUTE_KEY_LIBRARY_NAME,
+ METRIC_ATTRIBUTE_KEY_LIBRARY_VERSION,
+ METRIC_ATTRIBUTE_KEY_STATUS,
+ METRIC_ATTRIBUTE_KEY_METHOD);
+
+ // Metric names
+ String METRIC_NAME_OPERATION_LATENCY = "operation_latency";
+ String METRIC_NAME_OPERATION_COUNT = "operation_count";
+ String METRIC_NAME_ATTEMPT_LATENCY = "attempt_latency";
+ String METRIC_NAME_ATTEMPT_COUNT = "attempt_count";
+ String METRIC_NAME_FIRST_RESPONSE_LATENCY = "first_response_latency";
+ String METRIC_NAME_END_TO_END_LATENCY = "end_to_end_latency";
+ String METRIC_NAME_TRANSACTION_LATENCY = "transaction_latency";
+ String METRIC_NAME_TRANSACTION_ATTEMPT_COUNT = "transaction_attempt_count";
+
+ // Metrics collected on GAX and Firestore SDK layer
+ Set GAX_METRICS =
+ ImmutableSet.of(
+ METRIC_NAME_OPERATION_LATENCY,
+ METRIC_NAME_ATTEMPT_LATENCY,
+ METRIC_NAME_OPERATION_COUNT,
+ METRIC_NAME_ATTEMPT_COUNT);
+
+ Set FIRESTORE_METRICS =
+ ImmutableSet.of(
+ METRIC_NAME_FIRST_RESPONSE_LATENCY,
+ METRIC_NAME_END_TO_END_LATENCY,
+ METRIC_NAME_TRANSACTION_LATENCY,
+ METRIC_NAME_TRANSACTION_ATTEMPT_COUNT);
+
+ public enum MetricType {
+ END_TO_END_LATENCY,
+ FIRST_RESPONSE_LATENCY,
+ TRANSACTION_LATENCY,
+ TRANSACTION_ATTEMPT_COUNT
+ }
+}
diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TraceUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TraceUtil.java
index 22dfb3ed0c..847eff58ac 100644
--- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TraceUtil.java
+++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/TraceUtil.java
@@ -18,6 +18,7 @@
import com.google.api.core.ApiFunction;
import com.google.api.core.ApiFuture;
+import com.google.api.core.InternalApi;
import com.google.cloud.firestore.FirestoreOptions;
import io.grpc.ManagedChannelBuilder;
import java.util.Map;
@@ -31,30 +32,6 @@
*/
public interface TraceUtil {
String ATTRIBUTE_SERVICE_PREFIX = "gcp.firestore.";
- String SPAN_NAME_DOC_REF_CREATE = "DocumentReference.Create";
- String SPAN_NAME_DOC_REF_SET = "DocumentReference.Set";
- String SPAN_NAME_DOC_REF_UPDATE = "DocumentReference.Update";
- String SPAN_NAME_DOC_REF_DELETE = "DocumentReference.Delete";
- String SPAN_NAME_DOC_REF_GET = "DocumentReference.Get";
- String SPAN_NAME_DOC_REF_LIST_COLLECTIONS = "DocumentReference.ListCollections";
- String SPAN_NAME_COL_REF_ADD = "CollectionReference.Add";
- String SPAN_NAME_COL_REF_LIST_DOCUMENTS = "CollectionReference.ListDocuments";
- String SPAN_NAME_QUERY_GET = "Query.Get";
- String SPAN_NAME_AGGREGATION_QUERY_GET = "AggregationQuery.Get";
- String SPAN_NAME_RUN_QUERY = "RunQuery";
- String SPAN_NAME_RUN_AGGREGATION_QUERY = "RunAggregationQuery";
- String SPAN_NAME_BATCH_GET_DOCUMENTS = "BatchGetDocuments";
- String SPAN_NAME_TRANSACTION_RUN = "Transaction.Run";
- String SPAN_NAME_TRANSACTION_BEGIN = "Transaction.Begin";
- String SPAN_NAME_TRANSACTION_GET_QUERY = "Transaction.Get.Query";
- String SPAN_NAME_TRANSACTION_GET_AGGREGATION_QUERY = "Transaction.Get.AggregationQuery";
- String SPAN_NAME_TRANSACTION_GET_DOCUMENT = "Transaction.Get.Document";
- String SPAN_NAME_TRANSACTION_GET_DOCUMENTS = "Transaction.Get.Documents";
- String SPAN_NAME_TRANSACTION_ROLLBACK = "Transaction.Rollback";
- String SPAN_NAME_BATCH_COMMIT = "Batch.Commit";
- String SPAN_NAME_TRANSACTION_COMMIT = "Transaction.Commit";
- String SPAN_NAME_PARTITION_QUERY = "PartitionQuery";
- String SPAN_NAME_BULK_WRITER_COMMIT = "BulkWriter.Commit";
String ATTRIBUTE_KEY_ATTEMPT = "attempt";
String ATTRIBUTE_KEY_DOC_COUNT = "doc_count";
String ATTRIBUTE_KEY_IS_TRANSACTIONAL = "transactional";
@@ -74,8 +51,9 @@ public interface TraceUtil {
* TraceUtil.
* @return An instance of the TraceUtil class.
*/
+ @InternalApi
static TraceUtil getInstance(@Nonnull FirestoreOptions firestoreOptions) {
- boolean createEnabledInstance = firestoreOptions.getOpenTelemetryOptions().isTracingEnabled();
+ boolean createEnabledInstance = true;
// The environment variable can override options to enable/disable telemetry collection.
String enableTracingEnvVar = System.getenv(ENABLE_TRACING_ENV_VAR);
diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/OpenTelemetryOptionsTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/OpenTelemetryOptionsTest.java
index 267020761c..009e2457b8 100644
--- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/OpenTelemetryOptionsTest.java
+++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/OpenTelemetryOptionsTest.java
@@ -18,11 +18,12 @@
import static com.google.common.truth.Truth.assertThat;
-import com.google.cloud.firestore.telemetry.DisabledTraceUtil;
import com.google.cloud.firestore.telemetry.EnabledTraceUtil;
import io.opentelemetry.api.GlobalOpenTelemetry;
-import io.opentelemetry.api.OpenTelemetry;
+import io.opentelemetry.api.trace.TracerProvider;
import io.opentelemetry.sdk.OpenTelemetrySdk;
+import io.opentelemetry.sdk.resources.Resource;
+import io.opentelemetry.sdk.trace.SdkTracerProvider;
import javax.annotation.Nullable;
import org.junit.After;
import org.junit.Before;
@@ -49,112 +50,67 @@ FirestoreOptions.Builder getBaseOptions() {
}
@Test
- public void defaultOptionsDisablesTelemetryCollection() {
+ public void defaultOptionsUsesEnabledTraceUtilWithNoopOpenTelemetry() {
FirestoreOptions firestoreOptions = getBaseOptions().build();
firestore = firestoreOptions.getService();
- assertThat(firestore.getOptions().getOpenTelemetryOptions().isTracingEnabled()).isFalse();
assertThat(firestore.getOptions().getOpenTelemetryOptions().getOpenTelemetry()).isNull();
assertThat(firestore.getOptions().getTraceUtil()).isNotNull();
- assertThat(firestore.getOptions().getTraceUtil() instanceof DisabledTraceUtil).isTrue();
- }
-
- @Test
- public void canEnableTelemetryCollectionWithoutOpenTelemetryInstance() {
- FirestoreOptions firestoreOptions =
- getBaseOptions()
- .setOpenTelemetryOptions(
- FirestoreOpenTelemetryOptions.newBuilder().setTracingEnabled(true).build())
- .build();
- firestore = firestoreOptions.getService();
- assertThat(firestore.getOptions().getOpenTelemetryOptions().isTracingEnabled()).isTrue();
- assertThat(firestore.getOptions().getOpenTelemetryOptions().getOpenTelemetry()).isNull();
- assertThat(firestore.getOptions().getTraceUtil()).isNotNull();
- assertThat(firestore.getOptions().getTraceUtil() instanceof EnabledTraceUtil).isTrue();
- }
-
- @Test
- public void canEnableTelemetryCollectionWithOpenTelemetryInstance() {
- OpenTelemetry openTelemetry = GlobalOpenTelemetry.get();
- FirestoreOptions firestoreOptions =
- getBaseOptions()
- .setOpenTelemetryOptions(
- FirestoreOpenTelemetryOptions.newBuilder()
- .setTracingEnabled(true)
- .setOpenTelemetry(openTelemetry)
- .build())
- .build();
- firestore = firestoreOptions.getService();
- assertThat(firestore.getOptions().getOpenTelemetryOptions().isTracingEnabled()).isTrue();
- assertThat(firestore.getOptions().getOpenTelemetryOptions().getOpenTelemetry())
- .isEqualTo(openTelemetry);
- assertThat(firestore.getOptions().getTraceUtil()).isNotNull();
assertThat(firestore.getOptions().getTraceUtil() instanceof EnabledTraceUtil).isTrue();
+ EnabledTraceUtil enabledTraceUtil = (EnabledTraceUtil) firestore.getOptions().getTraceUtil();
+ // Assert that a Noop tracer provider is used by default.
+ assertThat(enabledTraceUtil.getOpenTelemetry().getTracerProvider())
+ .isSameInstanceAs(TracerProvider.noop());
}
@Test
- public void canDisableTelemetryCollectionWhileOpenTelemetryInstanceIsNotNull() {
- OpenTelemetry openTelemetry = GlobalOpenTelemetry.get();
- FirestoreOptions firestoreOptions =
- getBaseOptions()
- .setOpenTelemetryOptions(
- FirestoreOpenTelemetryOptions.newBuilder()
- .setTracingEnabled(false)
- .setOpenTelemetry(openTelemetry)
- .build())
- .build();
- firestore = firestoreOptions.getService();
- assertThat(firestore.getOptions().getOpenTelemetryOptions().isTracingEnabled()).isFalse();
- assertThat(firestore.getOptions().getOpenTelemetryOptions().getOpenTelemetry())
- .isEqualTo(openTelemetry);
- assertThat(firestore.getOptions().getTraceUtil()).isNotNull();
- assertThat(firestore.getOptions().getTraceUtil() instanceof DisabledTraceUtil).isTrue();
- }
+ public void existenceOfGlobalOpenTelemetryEnablesTracingWithTheGlobalTracerProvider() {
+ // Make a custom TracerProvider.
+ Resource resource =
+ Resource.getDefault().merge(Resource.builder().put("service.name", "test").build());
+ SdkTracerProvider myTracerProvider = SdkTracerProvider.builder().setResource(resource).build();
- @Test
- public void existenceOfGlobalOpenTelemetryDoesNotEnableTracing() {
- // Register a global OpenTelemetry SDK.
- OpenTelemetrySdk.builder().buildAndRegisterGlobal();
+ // Register a GlobalOpenTelemetry with the custom tracer provider.
+ OpenTelemetrySdk.builder().setTracerProvider(myTracerProvider).buildAndRegisterGlobal();
- // Make sure Firestore does not use GlobalOpenTelemetry by default.
+ // Do NOT pass an OpenTelemetry instance to Firestore.
FirestoreOptions firestoreOptions = getBaseOptions().build();
firestore = firestoreOptions.getService();
- assertThat(firestore.getOptions().getOpenTelemetryOptions().isTracingEnabled()).isFalse();
+
+ // An OpenTelemetry instance has not been set in options.
assertThat(firestore.getOptions().getOpenTelemetryOptions().getOpenTelemetry()).isNull();
+
+ // Assert that tracing is enabled and is using the custom tracer provider from the
+ // GlobalOpenTelemetry.
assertThat(firestore.getOptions().getTraceUtil()).isNotNull();
- assertThat(firestore.getOptions().getTraceUtil() instanceof DisabledTraceUtil).isTrue();
+ assertThat(firestore.getOptions().getTraceUtil() instanceof EnabledTraceUtil).isTrue();
+ EnabledTraceUtil enabledTraceUtil = (EnabledTraceUtil) firestore.getOptions().getTraceUtil();
+ assertThat(enabledTraceUtil.getOpenTelemetry().getTracerProvider().equals(myTracerProvider));
}
@Test
public void canPassOpenTelemetrySdkInstanceToFirestore() {
- OpenTelemetrySdk myOpenTelemetrySdk = OpenTelemetrySdk.builder().build();
+ // Make a custom TracerProvider and make an OpenTelemetry instance with it.
+ Resource resource =
+ Resource.getDefault().merge(Resource.builder().put("service.name", "test").build());
+ SdkTracerProvider myTracerProvider = SdkTracerProvider.builder().setResource(resource).build();
+ OpenTelemetrySdk myOpenTelemetrySdk =
+ OpenTelemetrySdk.builder().setTracerProvider(myTracerProvider).build();
+
+ // Pass it to Firestore.
FirestoreOptions firestoreOptions =
getBaseOptions()
.setOpenTelemetryOptions(
FirestoreOpenTelemetryOptions.newBuilder()
- .setTracingEnabled(true)
.setOpenTelemetry(myOpenTelemetrySdk)
.build())
.build();
firestore = firestoreOptions.getService();
+ assertThat(firestore.getOptions().getOpenTelemetryOptions().getOpenTelemetry())
+ .isEqualTo(myOpenTelemetrySdk);
+ assertThat(firestore.getOptions().getTraceUtil() instanceof EnabledTraceUtil).isTrue();
EnabledTraceUtil enabledTraceUtil = (EnabledTraceUtil) firestore.getOptions().getTraceUtil();
assertThat(enabledTraceUtil).isNotNull();
assertThat(enabledTraceUtil.getOpenTelemetry()).isEqualTo(myOpenTelemetrySdk);
- }
-
- @Test
- public void usesGlobalOpenTelemetryIfOpenTelemetryNotProvidedInOptions() {
- // Register a global OpenTelemetry SDK.
- OpenTelemetrySdk.builder().buildAndRegisterGlobal();
-
- // Do _not_ pass it to FirestoreOptions.
- FirestoreOptions firestoreOptions =
- getBaseOptions()
- .setOpenTelemetryOptions(
- FirestoreOpenTelemetryOptions.newBuilder().setTracingEnabled(true).build())
- .build();
- firestore = firestoreOptions.getService();
- EnabledTraceUtil enabledTraceUtil = (EnabledTraceUtil) firestore.getOptions().getTraceUtil();
- assertThat(enabledTraceUtil).isNotNull();
- assertThat(enabledTraceUtil.getOpenTelemetry()).isEqualTo(GlobalOpenTelemetry.get());
+ assertThat(enabledTraceUtil.getOpenTelemetry().getTracerProvider().equals(myTracerProvider));
}
}
diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/WatchTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/WatchTest.java
index 19295b9018..d875c6411b 100644
--- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/WatchTest.java
+++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/WatchTest.java
@@ -24,11 +24,11 @@
import static com.google.cloud.firestore.LocalFirestoreHelper.UPDATED_FIELD_PROTO;
import static com.google.cloud.firestore.LocalFirestoreHelper.map;
import static com.google.cloud.firestore.LocalFirestoreHelper.string;
+import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
-import static org.junit.Assert.assertTrue;
import static org.mockito.Mockito.*;
import com.google.api.gax.grpc.GrpcStatusCode;
@@ -169,10 +169,11 @@ public void before() {
@After
public void after() {
- assertTrue(exceptions.isEmpty());
- assertTrue(requests.isEmpty());
- assertTrue(documentSnapshots.isEmpty());
- assertTrue(querySnapshots.isEmpty());
+ Object[] emptyArray = new Object[0];
+ assertArrayEquals(exceptions.toArray(), emptyArray);
+ assertArrayEquals(requests.toArray(), emptyArray);
+ assertArrayEquals(documentSnapshots.toArray(), emptyArray);
+ assertArrayEquals(querySnapshots.toArray(), emptyArray);
listenerRegistration.remove();
}
diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2ETracingTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2ETracingTest.java
index 3d385e344f..82dbe99146 100644
--- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2ETracingTest.java
+++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITE2ETracingTest.java
@@ -16,24 +16,24 @@
package com.google.cloud.firestore.it;
-import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_BATCH_COMMIT;
-import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_BULK_WRITER_COMMIT;
-import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_COL_REF_LIST_DOCUMENTS;
-import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_DOC_REF_CREATE;
-import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_DOC_REF_DELETE;
-import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_DOC_REF_GET;
-import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_DOC_REF_LIST_COLLECTIONS;
-import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_DOC_REF_SET;
-import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_DOC_REF_UPDATE;
-import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_PARTITION_QUERY;
-import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_QUERY_GET;
-import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_TRANSACTION_BEGIN;
-import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_TRANSACTION_COMMIT;
-import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_TRANSACTION_GET_AGGREGATION_QUERY;
-import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_TRANSACTION_GET_DOCUMENTS;
-import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_TRANSACTION_GET_QUERY;
-import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_TRANSACTION_ROLLBACK;
-import static com.google.cloud.firestore.telemetry.TraceUtil.SPAN_NAME_TRANSACTION_RUN;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_BATCH_COMMIT;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_BULK_WRITER_COMMIT;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_COL_REF_LIST_DOCUMENTS;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_DOC_REF_CREATE;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_DOC_REF_DELETE;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_DOC_REF_GET;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_DOC_REF_LIST_COLLECTIONS;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_DOC_REF_SET;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_DOC_REF_UPDATE;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_PARTITION_QUERY;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_QUERY_GET;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_TRANSACTION_BEGIN;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_TRANSACTION_COMMIT;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_TRANSACTION_GET_AGGREGATION_QUERY;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_TRANSACTION_GET_DOCUMENTS;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_TRANSACTION_GET_QUERY;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_TRANSACTION_ROLLBACK;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.METHOD_NAME_TRANSACTION_RUN;
import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.SERVICE_NAME;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
@@ -319,20 +319,13 @@ public void before() throws Exception {
// Initialize the Firestore DB w/ the OTel SDK. Ideally we'd do this is the @BeforeAll method
// but because gRPC traces need to be deterministically force-flushed, firestore.shutdown()
// must be called in @After for each test.
- FirestoreOptions.Builder optionsBuilder;
- if (isUsingGlobalOpenTelemetrySDK()) {
- optionsBuilder =
- FirestoreOptions.newBuilder()
- .setOpenTelemetryOptions(
- FirestoreOpenTelemetryOptions.newBuilder().setTracingEnabled(true).build());
- } else {
+ FirestoreOptions.Builder optionsBuilder = FirestoreOptions.newBuilder();
+ if (!isUsingGlobalOpenTelemetrySDK()) {
optionsBuilder =
- FirestoreOptions.newBuilder()
- .setOpenTelemetryOptions(
- FirestoreOpenTelemetryOptions.newBuilder()
- .setOpenTelemetry(openTelemetrySdk)
- .setTracingEnabled(true)
- .build());
+ optionsBuilder.setOpenTelemetryOptions(
+ FirestoreOpenTelemetryOptions.newBuilder()
+ .setOpenTelemetry(openTelemetrySdk)
+ .build());
}
String namedDb = System.getProperty("FIRESTORE_NAMED_DATABASE");
@@ -559,18 +552,18 @@ public void traceContainerTest() throws Exception {
// Contains exact path
assertTrue(
traceCont.containsCallStack(
- rootSpanName, SPAN_NAME_QUERY_GET, grpcSpanName(RUN_QUERY_RPC_NAME)));
+ rootSpanName, METHOD_NAME_QUERY_GET, grpcSpanName(RUN_QUERY_RPC_NAME)));
// Top-level mismatch
- assertFalse(traceCont.containsCallStack(SPAN_NAME_QUERY_GET, RUN_QUERY_RPC_NAME));
+ assertFalse(traceCont.containsCallStack(METHOD_NAME_QUERY_GET, RUN_QUERY_RPC_NAME));
// Mid-level match
- assertFalse(traceCont.containsCallStack(rootSpanName, SPAN_NAME_QUERY_GET));
+ assertFalse(traceCont.containsCallStack(rootSpanName, METHOD_NAME_QUERY_GET));
// Leaf-level mismatch/missing
assertFalse(
traceCont.containsCallStack(
- rootSpanName, SPAN_NAME_QUERY_GET, RUN_AGGREGATION_QUERY_RPC_NAME));
+ rootSpanName, METHOD_NAME_QUERY_GET, RUN_AGGREGATION_QUERY_RPC_NAME));
}
@Test
@@ -621,7 +614,7 @@ public void bulkWriterCommitTraceTest() throws Exception {
// Read and validate traces
fetchAndValidateTrace(
customSpanContext.getTraceId(),
- SPAN_NAME_BULK_WRITER_COMMIT,
+ METHOD_NAME_BULK_WRITER_COMMIT,
grpcSpanName(BATCH_WRITE_RPC_NAME));
}
@@ -643,8 +636,8 @@ public void partitionQueryTraceTest() throws Exception {
// Read and validate traces
fetchAndValidateTrace(
customSpanContext.getTraceId(),
- SPAN_NAME_PARTITION_QUERY,
- grpcSpanName(SPAN_NAME_PARTITION_QUERY));
+ METHOD_NAME_PARTITION_QUERY,
+ grpcSpanName(METHOD_NAME_PARTITION_QUERY));
}
@Test
@@ -664,7 +657,7 @@ public void collectionListDocumentsTraceTest() throws Exception {
// Read and validate traces
fetchAndValidateTrace(
customSpanContext.getTraceId(),
- SPAN_NAME_COL_REF_LIST_DOCUMENTS,
+ METHOD_NAME_COL_REF_LIST_DOCUMENTS,
grpcSpanName(LIST_DOCUMENTS_RPC_NAME));
}
@@ -685,8 +678,8 @@ public void docRefCreateTraceTest() throws Exception {
// Read and validate traces
fetchAndValidateTrace(
customSpanContext.getTraceId(),
- SPAN_NAME_DOC_REF_CREATE,
- SPAN_NAME_BATCH_COMMIT,
+ METHOD_NAME_DOC_REF_CREATE,
+ METHOD_NAME_BATCH_COMMIT,
grpcSpanName(COMMIT_RPC_NAME));
}
@@ -707,8 +700,8 @@ public void docRefCreate2TraceTest() throws Exception {
// Read and validate traces
fetchAndValidateTrace(
customSpanContext.getTraceId(),
- SPAN_NAME_DOC_REF_CREATE,
- SPAN_NAME_BATCH_COMMIT,
+ METHOD_NAME_DOC_REF_CREATE,
+ METHOD_NAME_BATCH_COMMIT,
grpcSpanName(COMMIT_RPC_NAME));
}
@@ -729,8 +722,8 @@ public void docRefSetTraceTest() throws Exception {
// Read and validate traces
fetchAndValidateTrace(
customSpanContext.getTraceId(),
- SPAN_NAME_DOC_REF_SET,
- SPAN_NAME_BATCH_COMMIT,
+ METHOD_NAME_DOC_REF_SET,
+ METHOD_NAME_BATCH_COMMIT,
grpcSpanName(COMMIT_RPC_NAME));
}
@@ -755,8 +748,8 @@ public void docRefSet2TraceTest() throws Exception {
// Read and validate traces
fetchAndValidateTrace(
customSpanContext.getTraceId(),
- SPAN_NAME_DOC_REF_SET,
- SPAN_NAME_BATCH_COMMIT,
+ METHOD_NAME_DOC_REF_SET,
+ METHOD_NAME_BATCH_COMMIT,
grpcSpanName(COMMIT_RPC_NAME));
}
@@ -777,8 +770,8 @@ public void docRefSet3TraceTest() throws Exception {
// Read and validate traces
fetchAndValidateTrace(
customSpanContext.getTraceId(),
- SPAN_NAME_DOC_REF_SET,
- SPAN_NAME_BATCH_COMMIT,
+ METHOD_NAME_DOC_REF_SET,
+ METHOD_NAME_BATCH_COMMIT,
grpcSpanName(COMMIT_RPC_NAME));
}
@@ -799,8 +792,8 @@ public void docRefSet4TraceTest() throws Exception {
// Read and validate traces
fetchAndValidateTrace(
customSpanContext.getTraceId(),
- SPAN_NAME_DOC_REF_SET,
- SPAN_NAME_BATCH_COMMIT,
+ METHOD_NAME_DOC_REF_SET,
+ METHOD_NAME_BATCH_COMMIT,
grpcSpanName(COMMIT_RPC_NAME));
}
@@ -825,8 +818,8 @@ public void docRefUpdateTraceTest() throws Exception {
// Read and validate traces
fetchAndValidateTrace(
customSpanContext.getTraceId(),
- SPAN_NAME_DOC_REF_UPDATE,
- SPAN_NAME_BATCH_COMMIT,
+ METHOD_NAME_DOC_REF_UPDATE,
+ METHOD_NAME_BATCH_COMMIT,
grpcSpanName(COMMIT_RPC_NAME));
}
@@ -851,8 +844,8 @@ public void docRefUpdate2TraceTest() throws Exception {
// Read and validate traces
fetchAndValidateTrace(
customSpanContext.getTraceId(),
- SPAN_NAME_DOC_REF_UPDATE,
- SPAN_NAME_BATCH_COMMIT,
+ METHOD_NAME_DOC_REF_UPDATE,
+ METHOD_NAME_BATCH_COMMIT,
grpcSpanName(COMMIT_RPC_NAME));
}
@@ -873,8 +866,8 @@ public void docRefUpdate3TraceTest() throws Exception {
// Read and validate traces
fetchAndValidateTrace(
customSpanContext.getTraceId(),
- SPAN_NAME_DOC_REF_UPDATE,
- SPAN_NAME_BATCH_COMMIT,
+ METHOD_NAME_DOC_REF_UPDATE,
+ METHOD_NAME_BATCH_COMMIT,
grpcSpanName(COMMIT_RPC_NAME));
}
@@ -899,8 +892,8 @@ public void docRefUpdate4TraceTest() throws Exception {
// Read and validate traces
fetchAndValidateTrace(
customSpanContext.getTraceId(),
- SPAN_NAME_DOC_REF_UPDATE,
- SPAN_NAME_BATCH_COMMIT,
+ METHOD_NAME_DOC_REF_UPDATE,
+ METHOD_NAME_BATCH_COMMIT,
grpcSpanName(COMMIT_RPC_NAME));
}
@@ -925,8 +918,8 @@ public void docRefUpdate5TraceTest() throws Exception {
// Read and validate traces
fetchAndValidateTrace(
customSpanContext.getTraceId(),
- SPAN_NAME_DOC_REF_UPDATE,
- SPAN_NAME_BATCH_COMMIT,
+ METHOD_NAME_DOC_REF_UPDATE,
+ METHOD_NAME_BATCH_COMMIT,
grpcSpanName(COMMIT_RPC_NAME));
}
@@ -951,8 +944,8 @@ public void docRefUpdate6TraceTest() throws Exception {
// Read and validate traces
fetchAndValidateTrace(
customSpanContext.getTraceId(),
- SPAN_NAME_DOC_REF_UPDATE,
- SPAN_NAME_BATCH_COMMIT,
+ METHOD_NAME_DOC_REF_UPDATE,
+ METHOD_NAME_BATCH_COMMIT,
grpcSpanName(COMMIT_RPC_NAME));
}
@@ -973,8 +966,8 @@ public void docRefDeleteTraceTest() throws Exception {
// Read and validate traces
fetchAndValidateTrace(
customSpanContext.getTraceId(),
- SPAN_NAME_DOC_REF_DELETE,
- SPAN_NAME_BATCH_COMMIT,
+ METHOD_NAME_DOC_REF_DELETE,
+ METHOD_NAME_BATCH_COMMIT,
grpcSpanName(COMMIT_RPC_NAME));
}
@@ -995,8 +988,8 @@ public void docRefDelete2TraceTest() throws Exception {
// Read and validate traces
fetchAndValidateTrace(
customSpanContext.getTraceId(),
- SPAN_NAME_DOC_REF_DELETE,
- SPAN_NAME_BATCH_COMMIT,
+ METHOD_NAME_DOC_REF_DELETE,
+ METHOD_NAME_BATCH_COMMIT,
grpcSpanName(COMMIT_RPC_NAME));
}
@@ -1017,7 +1010,7 @@ public void docRefGetTraceTest() throws Exception {
// Read and validate traces
fetchAndValidateTrace(
customSpanContext.getTraceId(),
- SPAN_NAME_DOC_REF_GET,
+ METHOD_NAME_DOC_REF_GET,
grpcSpanName(BATCH_GET_DOCUMENTS_RPC_NAME));
}
@@ -1038,7 +1031,7 @@ public void docRefGet2TraceTest() throws Exception {
// Read and validate traces
fetchAndValidateTrace(
customSpanContext.getTraceId(),
- SPAN_NAME_DOC_REF_GET,
+ METHOD_NAME_DOC_REF_GET,
grpcSpanName(BATCH_GET_DOCUMENTS_RPC_NAME));
}
@@ -1059,7 +1052,7 @@ public void docListCollectionsTraceTest() throws Exception {
// Read and validate traces
fetchAndValidateTrace(
customSpanContext.getTraceId(),
- SPAN_NAME_DOC_REF_LIST_COLLECTIONS,
+ METHOD_NAME_DOC_REF_LIST_COLLECTIONS,
grpcSpanName(LIST_COLLECTIONS_RPC_NAME));
}
@@ -1099,7 +1092,7 @@ public void queryGetTraceTest() throws Exception {
waitForTracesToComplete();
fetchAndValidateTrace(
- customSpanContext.getTraceId(), SPAN_NAME_QUERY_GET, grpcSpanName(RUN_QUERY_RPC_NAME));
+ customSpanContext.getTraceId(), METHOD_NAME_QUERY_GET, grpcSpanName(RUN_QUERY_RPC_NAME));
}
@Test
@@ -1144,28 +1137,28 @@ public void transactionTraceTest() throws Exception {
/*numExpectedSpans=*/ 11,
Arrays.asList(
Arrays.asList(
- SPAN_NAME_TRANSACTION_RUN,
- SPAN_NAME_TRANSACTION_BEGIN,
+ METHOD_NAME_TRANSACTION_RUN,
+ METHOD_NAME_TRANSACTION_BEGIN,
grpcSpanName(BEGIN_TRANSACTION_RPC_NAME)),
Arrays.asList(
- SPAN_NAME_TRANSACTION_RUN,
- SPAN_NAME_TRANSACTION_BEGIN,
+ METHOD_NAME_TRANSACTION_RUN,
+ METHOD_NAME_TRANSACTION_BEGIN,
grpcSpanName(BEGIN_TRANSACTION_RPC_NAME)),
Arrays.asList(
- SPAN_NAME_TRANSACTION_RUN,
- SPAN_NAME_TRANSACTION_GET_QUERY,
+ METHOD_NAME_TRANSACTION_RUN,
+ METHOD_NAME_TRANSACTION_GET_QUERY,
grpcSpanName(RUN_QUERY_RPC_NAME)),
Arrays.asList(
- SPAN_NAME_TRANSACTION_RUN,
- SPAN_NAME_TRANSACTION_GET_AGGREGATION_QUERY,
+ METHOD_NAME_TRANSACTION_RUN,
+ METHOD_NAME_TRANSACTION_GET_AGGREGATION_QUERY,
grpcSpanName(RUN_AGGREGATION_QUERY_RPC_NAME)),
Arrays.asList(
- SPAN_NAME_TRANSACTION_RUN,
- SPAN_NAME_TRANSACTION_GET_DOCUMENTS,
+ METHOD_NAME_TRANSACTION_RUN,
+ METHOD_NAME_TRANSACTION_GET_DOCUMENTS,
grpcSpanName(BATCH_GET_DOCUMENTS_RPC_NAME)),
Arrays.asList(
- SPAN_NAME_TRANSACTION_RUN,
- SPAN_NAME_TRANSACTION_COMMIT,
+ METHOD_NAME_TRANSACTION_RUN,
+ METHOD_NAME_TRANSACTION_COMMIT,
grpcSpanName(COMMIT_RPC_NAME))));
}
@@ -1199,12 +1192,12 @@ public void transactionRollbackTraceTest() throws Exception {
/*numExpectedSpans=*/ 5,
Arrays.asList(
Arrays.asList(
- SPAN_NAME_TRANSACTION_RUN,
- SPAN_NAME_TRANSACTION_BEGIN,
+ METHOD_NAME_TRANSACTION_RUN,
+ METHOD_NAME_TRANSACTION_BEGIN,
grpcSpanName(BEGIN_TRANSACTION_RPC_NAME)),
Arrays.asList(
- SPAN_NAME_TRANSACTION_RUN,
- SPAN_NAME_TRANSACTION_ROLLBACK,
+ METHOD_NAME_TRANSACTION_RUN,
+ METHOD_NAME_TRANSACTION_ROLLBACK,
grpcSpanName(ROLLBACK_RPC_NAME))));
}
@@ -1228,6 +1221,6 @@ public void writeBatchTraceTest() throws Exception {
waitForTracesToComplete();
fetchAndValidateTrace(
- customSpanContext.getTraceId(), SPAN_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME));
+ customSpanContext.getTraceId(), METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME));
}
}
diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITTracingTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITTracingTest.java
index 46e8294e1c..225ff01622 100644
--- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITTracingTest.java
+++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/it/ITTracingTest.java
@@ -16,6 +16,7 @@
package com.google.cloud.firestore.it;
+import static com.google.cloud.firestore.telemetry.TelemetryConstants.*;
import static com.google.cloud.firestore.telemetry.TraceUtil.*;
import static io.opentelemetry.semconv.resource.attributes.ResourceAttributes.SERVICE_NAME;
import static org.junit.Assert.assertEquals;
@@ -125,11 +126,11 @@ public void before() {
if (isUsingGlobalOpenTelemetrySDK()) {
GlobalOpenTelemetry.resetForTest();
openTelemetrySdk = openTelemetrySdkBuilder.buildAndRegisterGlobal();
- optionsBuilder.setOpenTelemetryOptions(otelOptionsBuilder.setTracingEnabled(true).build());
+ optionsBuilder.setOpenTelemetryOptions(otelOptionsBuilder.build());
} else {
openTelemetrySdk = openTelemetrySdkBuilder.build();
optionsBuilder.setOpenTelemetryOptions(
- otelOptionsBuilder.setTracingEnabled(true).setOpenTelemetry(openTelemetrySdk).build());
+ otelOptionsBuilder.setOpenTelemetry(openTelemetrySdk).build());
}
String namedDb = System.getProperty("FIRESTORE_NAMED_DATABASE");
@@ -365,8 +366,8 @@ public void aggregateQueryGet() throws Exception {
List spans = inMemorySpanExporter.getFinishedSpanItems();
buildSpanMaps(spans);
assertEquals(2, spans.size());
- SpanData getSpan = getSpanByName(SPAN_NAME_AGGREGATION_QUERY_GET);
- SpanData grpcSpan = getGrpcSpanByName(SPAN_NAME_RUN_AGGREGATION_QUERY);
+ SpanData getSpan = getSpanByName(METHOD_NAME_AGGREGATION_QUERY_GET);
+ SpanData grpcSpan = getGrpcSpanByName(METHOD_NAME_RUN_AGGREGATION_QUERY);
assertNotNull(getSpan);
assertNotNull(grpcSpan);
assertEquals(grpcSpan.getParentSpanId(), getSpan.getSpanId());
@@ -394,7 +395,7 @@ public void bulkWriterCommit() throws Exception {
List spans = prepareSpans();
assertEquals(2, spans.size());
- assertSpanHierarchy(SPAN_NAME_BULK_WRITER_COMMIT, grpcSpanName(BATCH_WRITE_RPC_NAME));
+ assertSpanHierarchy(METHOD_NAME_BULK_WRITER_COMMIT, grpcSpanName(BATCH_WRITE_RPC_NAME));
}
@Test
@@ -404,7 +405,7 @@ public void partitionQuery() throws Exception {
List spans = prepareSpans();
assertEquals(2, spans.size());
- assertSpanHierarchy(SPAN_NAME_PARTITION_QUERY, grpcSpanName(SPAN_NAME_PARTITION_QUERY));
+ assertSpanHierarchy(METHOD_NAME_PARTITION_QUERY, grpcSpanName(METHOD_NAME_PARTITION_QUERY));
}
@Test
@@ -413,7 +414,7 @@ public void collectionListDocuments() throws Exception {
List spans = prepareSpans();
assertEquals(2, spans.size());
- assertSpanHierarchy(SPAN_NAME_COL_REF_LIST_DOCUMENTS, grpcSpanName(LIST_DOCUMENTS_RPC_NAME));
+ assertSpanHierarchy(METHOD_NAME_COL_REF_LIST_DOCUMENTS, grpcSpanName(LIST_DOCUMENTS_RPC_NAME));
}
@Test
@@ -423,7 +424,7 @@ public void docRefCreate() throws Exception {
List spans = prepareSpans();
assertEquals(3, spans.size());
assertSpanHierarchy(
- SPAN_NAME_DOC_REF_CREATE, SPAN_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME));
+ METHOD_NAME_DOC_REF_CREATE, METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME));
}
@Test
@@ -433,7 +434,7 @@ public void docRefCreate2() throws Exception {
List spans = prepareSpans();
assertEquals(3, spans.size());
assertSpanHierarchy(
- SPAN_NAME_DOC_REF_CREATE, SPAN_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME));
+ METHOD_NAME_DOC_REF_CREATE, METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME));
}
@Test
@@ -443,7 +444,7 @@ public void docRefSet() throws Exception {
List spans = prepareSpans();
assertEquals(3, spans.size());
assertSpanHierarchy(
- SPAN_NAME_DOC_REF_SET, SPAN_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME));
+ METHOD_NAME_DOC_REF_SET, METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME));
}
@Test
@@ -457,7 +458,7 @@ public void docRefSet2() throws Exception {
List spans = prepareSpans();
assertEquals(3, spans.size());
assertSpanHierarchy(
- SPAN_NAME_DOC_REF_SET, SPAN_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME));
+ METHOD_NAME_DOC_REF_SET, METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME));
}
@Test
@@ -467,7 +468,7 @@ public void docRefSet3() throws Exception {
List spans = prepareSpans();
assertEquals(3, spans.size());
assertSpanHierarchy(
- SPAN_NAME_DOC_REF_SET, SPAN_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME));
+ METHOD_NAME_DOC_REF_SET, METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME));
}
@Test
@@ -477,7 +478,7 @@ public void docRefSet4() throws Exception {
List spans = prepareSpans();
assertEquals(3, spans.size());
assertSpanHierarchy(
- SPAN_NAME_DOC_REF_SET, SPAN_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME));
+ METHOD_NAME_DOC_REF_SET, METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME));
}
@Test
@@ -491,7 +492,7 @@ public void docRefUpdate() throws Exception {
List spans = prepareSpans();
assertEquals(3, spans.size());
assertSpanHierarchy(
- SPAN_NAME_DOC_REF_UPDATE, SPAN_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME));
+ METHOD_NAME_DOC_REF_UPDATE, METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME));
}
@Test
@@ -505,7 +506,7 @@ public void docRefUpdate2() throws Exception {
List spans = prepareSpans();
assertEquals(3, spans.size());
assertSpanHierarchy(
- SPAN_NAME_DOC_REF_UPDATE, SPAN_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME));
+ METHOD_NAME_DOC_REF_UPDATE, METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME));
}
@Test
@@ -515,7 +516,7 @@ public void docRefUpdate3() throws Exception {
List spans = prepareSpans();
assertEquals(3, spans.size());
assertSpanHierarchy(
- SPAN_NAME_DOC_REF_UPDATE, SPAN_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME));
+ METHOD_NAME_DOC_REF_UPDATE, METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME));
}
@Test
@@ -529,7 +530,7 @@ public void docRefUpdate4() throws Exception {
List spans = prepareSpans();
assertEquals(3, spans.size());
assertSpanHierarchy(
- SPAN_NAME_DOC_REF_UPDATE, SPAN_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME));
+ METHOD_NAME_DOC_REF_UPDATE, METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME));
}
@Test
@@ -543,7 +544,7 @@ public void docRefUpdate5() throws Exception {
List spans = prepareSpans();
assertEquals(3, spans.size());
assertSpanHierarchy(
- SPAN_NAME_DOC_REF_UPDATE, SPAN_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME));
+ METHOD_NAME_DOC_REF_UPDATE, METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME));
}
@Test
@@ -557,7 +558,7 @@ public void docRefUpdate6() throws Exception {
List spans = prepareSpans();
assertEquals(3, spans.size());
assertSpanHierarchy(
- SPAN_NAME_DOC_REF_UPDATE, SPAN_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME));
+ METHOD_NAME_DOC_REF_UPDATE, METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME));
}
@Test
@@ -567,7 +568,7 @@ public void docRefDelete() throws Exception {
List spans = prepareSpans();
assertEquals(3, spans.size());
assertSpanHierarchy(
- SPAN_NAME_DOC_REF_DELETE, SPAN_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME));
+ METHOD_NAME_DOC_REF_DELETE, METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME));
}
@Test
@@ -577,7 +578,7 @@ public void docRefDelete2() throws Exception {
List spans = prepareSpans();
assertEquals(3, spans.size());
assertSpanHierarchy(
- SPAN_NAME_DOC_REF_DELETE, SPAN_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME));
+ METHOD_NAME_DOC_REF_DELETE, METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME));
}
@Test
@@ -586,7 +587,7 @@ public void docRefGet() throws Exception {
List spans = prepareSpans();
assertEquals(2, spans.size());
- assertSpanHierarchy(SPAN_NAME_DOC_REF_GET, grpcSpanName(BATCH_GET_DOCUMENTS_RPC_NAME));
+ assertSpanHierarchy(METHOD_NAME_DOC_REF_GET, grpcSpanName(BATCH_GET_DOCUMENTS_RPC_NAME));
}
@Test
@@ -595,7 +596,7 @@ public void docRefGet2() throws Exception {
List spans = prepareSpans();
assertEquals(2, spans.size());
- assertSpanHierarchy(SPAN_NAME_DOC_REF_GET, grpcSpanName(BATCH_GET_DOCUMENTS_RPC_NAME));
+ assertSpanHierarchy(METHOD_NAME_DOC_REF_GET, grpcSpanName(BATCH_GET_DOCUMENTS_RPC_NAME));
}
@Test
@@ -605,7 +606,7 @@ public void docListCollections() throws Exception {
List spans = prepareSpans();
assertEquals(2, spans.size());
assertSpanHierarchy(
- SPAN_NAME_DOC_REF_LIST_COLLECTIONS, grpcSpanName(LIST_COLLECTIONS_RPC_NAME));
+ METHOD_NAME_DOC_REF_LIST_COLLECTIONS, grpcSpanName(LIST_COLLECTIONS_RPC_NAME));
}
@Test
@@ -630,8 +631,8 @@ public void queryGet() throws Exception {
firestore.collection("col").whereEqualTo("foo", "my_non_existent_value").get().get();
List spans = prepareSpans();
assertEquals(2, spans.size());
- assertSpanHierarchy(SPAN_NAME_QUERY_GET, grpcSpanName(RUN_QUERY_RPC_NAME));
- SpanData span = getSpanByName(SPAN_NAME_QUERY_GET);
+ assertSpanHierarchy(METHOD_NAME_QUERY_GET, grpcSpanName(RUN_QUERY_RPC_NAME));
+ SpanData span = getSpanByName(METHOD_NAME_QUERY_GET);
assertTrue(
hasEvent(
span,
@@ -687,32 +688,33 @@ public void transaction() throws Exception {
List spans = prepareSpans();
assertEquals(11, spans.size());
assertSpanHierarchy(
- SPAN_NAME_TRANSACTION_RUN,
- SPAN_NAME_TRANSACTION_BEGIN,
+ METHOD_NAME_TRANSACTION_RUN,
+ METHOD_NAME_TRANSACTION_BEGIN,
grpcSpanName(BEGIN_TRANSACTION_RPC_NAME));
assertSpanHierarchy(
- SPAN_NAME_TRANSACTION_RUN,
- SPAN_NAME_TRANSACTION_GET_QUERY,
+ METHOD_NAME_TRANSACTION_RUN,
+ METHOD_NAME_TRANSACTION_GET_QUERY,
grpcSpanName(RUN_QUERY_RPC_NAME));
assertSpanHierarchy(
- SPAN_NAME_TRANSACTION_RUN,
- SPAN_NAME_TRANSACTION_GET_AGGREGATION_QUERY,
+ METHOD_NAME_TRANSACTION_RUN,
+ METHOD_NAME_TRANSACTION_GET_AGGREGATION_QUERY,
grpcSpanName(RUN_AGGREGATION_QUERY_RPC_NAME));
assertSpanHierarchy(
- SPAN_NAME_TRANSACTION_RUN,
- SPAN_NAME_TRANSACTION_GET_DOCUMENTS,
+ METHOD_NAME_TRANSACTION_RUN,
+ METHOD_NAME_TRANSACTION_GET_DOCUMENTS,
grpcSpanName(BATCH_GET_DOCUMENTS_RPC_NAME));
assertSpanHierarchy(
- SPAN_NAME_TRANSACTION_RUN, SPAN_NAME_TRANSACTION_COMMIT, grpcSpanName(COMMIT_RPC_NAME));
+ METHOD_NAME_TRANSACTION_RUN, METHOD_NAME_TRANSACTION_COMMIT, grpcSpanName(COMMIT_RPC_NAME));
- Attributes commitAttributes = getSpanByName(SPAN_NAME_TRANSACTION_COMMIT).getAttributes();
+ Attributes commitAttributes = getSpanByName(METHOD_NAME_TRANSACTION_COMMIT).getAttributes();
assertEquals(
2L,
commitAttributes
.get(AttributeKey.longKey("gcp.firestore." + ATTRIBUTE_KEY_DOC_COUNT))
.longValue());
- Attributes runTransactionAttributes = getSpanByName(SPAN_NAME_TRANSACTION_RUN).getAttributes();
+ Attributes runTransactionAttributes =
+ getSpanByName(METHOD_NAME_TRANSACTION_RUN).getAttributes();
assertEquals(
5L,
runTransactionAttributes
@@ -754,13 +756,15 @@ public void transactionRollback() throws Exception {
List spans = prepareSpans();
assertEquals(5, spans.size());
assertSpanHierarchy(
- SPAN_NAME_TRANSACTION_RUN,
- SPAN_NAME_TRANSACTION_BEGIN,
+ METHOD_NAME_TRANSACTION_RUN,
+ METHOD_NAME_TRANSACTION_BEGIN,
grpcSpanName(BEGIN_TRANSACTION_RPC_NAME));
assertSpanHierarchy(
- SPAN_NAME_TRANSACTION_RUN, SPAN_NAME_TRANSACTION_ROLLBACK, grpcSpanName(ROLLBACK_RPC_NAME));
+ METHOD_NAME_TRANSACTION_RUN,
+ METHOD_NAME_TRANSACTION_ROLLBACK,
+ grpcSpanName(ROLLBACK_RPC_NAME));
- SpanData runTransactionSpanData = getSpanByName(SPAN_NAME_TRANSACTION_RUN);
+ SpanData runTransactionSpanData = getSpanByName(METHOD_NAME_TRANSACTION_RUN);
assertEquals(StatusCode.ERROR, runTransactionSpanData.getStatus().getStatusCode());
assertEquals(1, runTransactionSpanData.getEvents().size());
assertEquals(
@@ -797,16 +801,16 @@ public void writeBatch() throws Exception {
List spans = prepareSpans();
assertEquals(2, spans.size());
- assertSpanHierarchy(SPAN_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME));
+ assertSpanHierarchy(METHOD_NAME_BATCH_COMMIT, grpcSpanName(COMMIT_RPC_NAME));
assertEquals(
false,
- getSpanByName(SPAN_NAME_BATCH_COMMIT)
+ getSpanByName(METHOD_NAME_BATCH_COMMIT)
.getAttributes()
.get(AttributeKey.booleanKey("gcp.firestore." + ATTRIBUTE_KEY_IS_TRANSACTIONAL))
.booleanValue());
assertEquals(
3L,
- getSpanByName(SPAN_NAME_BATCH_COMMIT)
+ getSpanByName(METHOD_NAME_BATCH_COMMIT)
.getAttributes()
.get(AttributeKey.longKey("gcp.firestore." + ATTRIBUTE_KEY_DOC_COUNT))
.longValue());
diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledTraceUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledTraceUtilTest.java
index f131c30180..22e835f199 100644
--- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledTraceUtilTest.java
+++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/EnabledTraceUtilTest.java
@@ -20,6 +20,7 @@
import com.google.cloud.firestore.FirestoreOpenTelemetryOptions;
import com.google.cloud.firestore.FirestoreOptions;
import io.opentelemetry.api.GlobalOpenTelemetry;
+import io.opentelemetry.api.trace.TracerProvider;
import io.opentelemetry.sdk.OpenTelemetrySdk;
import org.junit.Before;
import org.junit.Test;
@@ -31,19 +32,12 @@ public void setUp() {
GlobalOpenTelemetry.resetForTest();
}
- FirestoreOptions.Builder getBaseOptions() {
- return FirestoreOptions.newBuilder().setProjectId("test-project").setDatabaseId("(default)");
+ EnabledTraceUtil defaultEnabledTraceUtil() {
+ return new EnabledTraceUtil(FirestoreOptions.getDefaultInstance());
}
- FirestoreOptions getTracingEnabledOptions() {
- return getBaseOptions()
- .setOpenTelemetryOptions(
- FirestoreOpenTelemetryOptions.newBuilder().setTracingEnabled(true).build())
- .build();
- }
-
- EnabledTraceUtil newEnabledTraceUtil() {
- return new EnabledTraceUtil(getTracingEnabledOptions());
+ FirestoreOptions.Builder getBaseOptions() {
+ return FirestoreOptions.newBuilder().setProjectId("test-project").setDatabaseId("(default)");
}
@Test
@@ -53,39 +47,79 @@ public void usesOpenTelemetryFromOptions() {
getBaseOptions()
.setOpenTelemetryOptions(
FirestoreOpenTelemetryOptions.newBuilder()
- .setTracingEnabled(true)
.setOpenTelemetry(myOpenTelemetrySdk)
.build())
.build();
EnabledTraceUtil traceUtil = new EnabledTraceUtil(firestoreOptions);
- assertThat(traceUtil.getOpenTelemetry()).isEqualTo(myOpenTelemetrySdk);
+ assertThat(traceUtil.getOpenTelemetry()).isSameInstanceAs(myOpenTelemetrySdk);
}
@Test
public void usesGlobalOpenTelemetryIfOpenTelemetryInstanceNotProvided() {
OpenTelemetrySdk.builder().buildAndRegisterGlobal();
+ EnabledTraceUtil traceUtil = defaultEnabledTraceUtil();
+ assertThat(traceUtil.getOpenTelemetry()).isSameInstanceAs(GlobalOpenTelemetry.get());
+ }
+
+ @Test
+ public void usesOpenTelemetryFromOptionsEvenIfGlobalOpenTelemetryExists() {
+ // Register a GlobalOpenTelemetry.
+ OpenTelemetrySdk.builder().buildAndRegisterGlobal();
+
+ // Pass in a *different* OpenTelemetry instance to Firestore to use.
+ OpenTelemetrySdk myOpenTelemetrySdk = OpenTelemetrySdk.builder().build();
FirestoreOptions firestoreOptions =
getBaseOptions()
.setOpenTelemetryOptions(
- FirestoreOpenTelemetryOptions.newBuilder().setTracingEnabled(true).build())
+ FirestoreOpenTelemetryOptions.newBuilder()
+ .setOpenTelemetry(myOpenTelemetrySdk)
+ .build())
.build();
EnabledTraceUtil traceUtil = new EnabledTraceUtil(firestoreOptions);
- assertThat(traceUtil.getOpenTelemetry()).isEqualTo(GlobalOpenTelemetry.get());
+
+ // Assert Firestore uses the custom one, not the global one.
+ assertThat(traceUtil.getOpenTelemetry()).isSameInstanceAs(myOpenTelemetrySdk);
+ assertThat(traceUtil.getOpenTelemetry()).isNotSameInstanceAs(GlobalOpenTelemetry.get());
}
@Test
- public void enabledTraceUtilProvidesChannelConfigurator() {
- assertThat(newEnabledTraceUtil().getChannelConfigurator()).isNotNull();
+ public void defaultOptionsDoesNotRegisterGrpcChannelConfigurator() {
+ EnabledTraceUtil traceUtil = defaultEnabledTraceUtil();
+ assertThat(traceUtil.getOpenTelemetry().getTracerProvider())
+ .isSameInstanceAs(TracerProvider.noop());
+ assertThat(traceUtil.getChannelConfigurator()).isNull();
+ }
+
+ @Test
+ public void globalOpenTelemetryRegistersGrpcChannelConfigurator() {
+ OpenTelemetrySdk.builder().buildAndRegisterGlobal();
+ EnabledTraceUtil traceUtil = defaultEnabledTraceUtil();
+ assertThat(traceUtil.getChannelConfigurator()).isNotNull();
+ }
+
+ @Test
+ public void openTelemetryInstanceRegistersGrpcChannelConfigurator() {
+ OpenTelemetrySdk myOpenTelemetrySdk = OpenTelemetrySdk.builder().build();
+ FirestoreOptions firestoreOptions =
+ getBaseOptions()
+ .setOpenTelemetryOptions(
+ FirestoreOpenTelemetryOptions.newBuilder()
+ .setOpenTelemetry(myOpenTelemetrySdk)
+ .build())
+ .build();
+ EnabledTraceUtil traceUtil = new EnabledTraceUtil(firestoreOptions);
+ assertThat(traceUtil.getChannelConfigurator()).isNotNull();
}
@Test
public void usesEnabledContext() {
- assertThat(newEnabledTraceUtil().currentContext() instanceof EnabledTraceUtil.Context).isTrue();
+ assertThat(defaultEnabledTraceUtil().currentContext() instanceof EnabledTraceUtil.Context)
+ .isTrue();
}
@Test
public void usesEnabledSpan() {
- EnabledTraceUtil traceUtil = newEnabledTraceUtil();
+ EnabledTraceUtil traceUtil = defaultEnabledTraceUtil();
assertThat(traceUtil.currentSpan() instanceof EnabledTraceUtil.Span).isTrue();
assertThat(traceUtil.startSpan("foo") instanceof EnabledTraceUtil.Span).isTrue();
assertThat(
@@ -95,14 +129,14 @@ public void usesEnabledSpan() {
@Test
public void usesEnabledScope() {
- EnabledTraceUtil traceUtil = newEnabledTraceUtil();
+ EnabledTraceUtil traceUtil = defaultEnabledTraceUtil();
assertThat(traceUtil.currentContext().makeCurrent() instanceof EnabledTraceUtil.Scope).isTrue();
assertThat(traceUtil.currentSpan().makeCurrent() instanceof EnabledTraceUtil.Scope).isTrue();
}
@Test
public void durationString() {
- EnabledTraceUtil traceUtil = newEnabledTraceUtil();
+ EnabledTraceUtil traceUtil = defaultEnabledTraceUtil();
Duration duration = Duration.ofSeconds(2, 9);
assertThat(traceUtil.durationString(duration)).isEqualTo("2.000000009s");
diff --git a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/TraceUtilTest.java b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/TraceUtilTest.java
index 5bb3be668c..e2c20b1dfe 100644
--- a/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/TraceUtilTest.java
+++ b/google-cloud-firestore/src/test/java/com/google/cloud/firestore/telemetry/TraceUtilTest.java
@@ -17,45 +17,17 @@
import static com.google.common.truth.Truth.assertThat;
-import com.google.cloud.firestore.FirestoreOpenTelemetryOptions;
import com.google.cloud.firestore.FirestoreOptions;
+import io.opentelemetry.api.trace.TracerProvider;
import org.junit.Test;
public class TraceUtilTest {
@Test
- public void defaultOptionsUseDisabledTraceUtil() {
- TraceUtil traceUtil =
- TraceUtil.getInstance(
- FirestoreOptions.newBuilder()
- .setProjectId("test-project")
- .setDatabaseId("(default)")
- .build());
- assertThat(traceUtil instanceof DisabledTraceUtil).isTrue();
- }
-
- @Test
- public void tracingDisabledOptionsUseDisabledTraceUtil() {
- TraceUtil traceUtil =
- TraceUtil.getInstance(
- FirestoreOptions.newBuilder()
- .setProjectId("test-project")
- .setDatabaseId("(default)")
- .setOpenTelemetryOptions(
- FirestoreOpenTelemetryOptions.newBuilder().setTracingEnabled(false).build())
- .build());
- assertThat(traceUtil instanceof DisabledTraceUtil).isTrue();
- }
-
- @Test
- public void tracingEnabledOptionsUseEnabledTraceUtil() {
- TraceUtil traceUtil =
- TraceUtil.getInstance(
- FirestoreOptions.newBuilder()
- .setProjectId("test-project")
- .setDatabaseId("(default)")
- .setOpenTelemetryOptions(
- FirestoreOpenTelemetryOptions.newBuilder().setTracingEnabled(true).build())
- .build());
+ public void defaultOptionsUseNoopTracer() {
+ TraceUtil traceUtil = TraceUtil.getInstance(FirestoreOptions.getDefaultInstance());
assertThat(traceUtil instanceof EnabledTraceUtil).isTrue();
+ EnabledTraceUtil enabledTraceUtil = (EnabledTraceUtil) traceUtil;
+ assertThat(
+ enabledTraceUtil.getOpenTelemetry().getTracerProvider().equals(TracerProvider.noop()));
}
}
diff --git a/grpc-google-cloud-firestore-admin-v1/pom.xml b/grpc-google-cloud-firestore-admin-v1/pom.xml
index 175a93eb11..4d394960ba 100644
--- a/grpc-google-cloud-firestore-admin-v1/pom.xml
+++ b/grpc-google-cloud-firestore-admin-v1/pom.xml
@@ -4,13 +4,13 @@
4.0.0
com.google.api.grpc
grpc-google-cloud-firestore-admin-v1
- 3.28.0
+ 3.29.0
grpc-google-cloud-firestore-admin-v1
GRPC library for grpc-google-cloud-firestore-admin-v1
com.google.cloud
google-cloud-firestore-parent
- 3.28.0
+ 3.29.0
diff --git a/grpc-google-cloud-firestore-v1/pom.xml b/grpc-google-cloud-firestore-v1/pom.xml
index 9f155fdfa3..4ae9fee30c 100644
--- a/grpc-google-cloud-firestore-v1/pom.xml
+++ b/grpc-google-cloud-firestore-v1/pom.xml
@@ -4,13 +4,13 @@
4.0.0
com.google.api.grpc
grpc-google-cloud-firestore-v1
- 3.28.0
+ 3.29.0
grpc-google-cloud-firestore-v1
GRPC library for grpc-google-cloud-firestore-v1
com.google.cloud
google-cloud-firestore-parent
- 3.28.0
+ 3.29.0
diff --git a/pom.xml b/pom.xml
index 6adc4d7cf4..26e165f1b8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
com.google.cloud
google-cloud-firestore-parent
pom
- 3.28.0
+ 3.29.0
Google Cloud Firestore Parent
https://github.com/googleapis/java-firestore
@@ -150,32 +150,32 @@
com.google.api.grpc
proto-google-cloud-firestore-admin-v1
- 3.28.0
+ 3.29.0
com.google.cloud
google-cloud-firestore
- 3.28.0
+ 3.29.0
com.google.cloud
proto-google-cloud-firestore-bundle-v1
- 3.28.0
+ 3.29.0
com.google.api.grpc
proto-google-cloud-firestore-v1
- 3.28.0
+ 3.29.0
com.google.api.grpc
grpc-google-cloud-firestore-admin-v1
- 3.28.0
+ 3.29.0
com.google.api.grpc
grpc-google-cloud-firestore-v1
- 3.28.0
+ 3.29.0
diff --git a/proto-google-cloud-firestore-admin-v1/pom.xml b/proto-google-cloud-firestore-admin-v1/pom.xml
index 243fca5150..fdcb0c7539 100644
--- a/proto-google-cloud-firestore-admin-v1/pom.xml
+++ b/proto-google-cloud-firestore-admin-v1/pom.xml
@@ -4,13 +4,13 @@
4.0.0
com.google.api.grpc
proto-google-cloud-firestore-admin-v1
- 3.28.0
+ 3.29.0
proto-google-cloud-firestore-admin-v1
PROTO library for proto-google-cloud-firestore-admin-v1
com.google.cloud
google-cloud-firestore-parent
- 3.28.0
+ 3.29.0
diff --git a/proto-google-cloud-firestore-bundle-v1/pom.xml b/proto-google-cloud-firestore-bundle-v1/pom.xml
index 86717eb901..97ac81c257 100644
--- a/proto-google-cloud-firestore-bundle-v1/pom.xml
+++ b/proto-google-cloud-firestore-bundle-v1/pom.xml
@@ -5,14 +5,14 @@
4.0.0
proto-google-cloud-firestore-bundle-v1
- 3.28.0
+ 3.29.0
proto-google-cloud-firestore-bundle-v1
PROTO library for proto-google-cloud-firestore-bundle-v1
com.google.cloud
google-cloud-firestore-parent
- 3.28.0
+ 3.29.0
diff --git a/proto-google-cloud-firestore-v1/pom.xml b/proto-google-cloud-firestore-v1/pom.xml
index 30fc436edc..bbee34f761 100644
--- a/proto-google-cloud-firestore-v1/pom.xml
+++ b/proto-google-cloud-firestore-v1/pom.xml
@@ -4,13 +4,13 @@
4.0.0
com.google.api.grpc
proto-google-cloud-firestore-v1
- 3.28.0
+ 3.29.0
proto-google-cloud-firestore-v1
PROTO library for proto-google-cloud-firestore-v1
com.google.cloud
google-cloud-firestore-parent
- 3.28.0
+ 3.29.0
diff --git a/samples/install-without-bom/pom.xml b/samples/install-without-bom/pom.xml
index edb5eeee4e..d5af9e33a0 100644
--- a/samples/install-without-bom/pom.xml
+++ b/samples/install-without-bom/pom.xml
@@ -30,7 +30,7 @@
com.google.cloud
google-cloud-firestore
- 3.27.4
+ 3.28.0
diff --git a/samples/snapshot/pom.xml b/samples/snapshot/pom.xml
index 0cc6325ddf..a565c68e33 100644
--- a/samples/snapshot/pom.xml
+++ b/samples/snapshot/pom.xml
@@ -29,7 +29,7 @@
com.google.cloud
google-cloud-firestore
- 3.27.4
+ 3.28.0
diff --git a/samples/snippets/pom.xml b/samples/snippets/pom.xml
index deb578101a..53fa89e02f 100644
--- a/samples/snippets/pom.xml
+++ b/samples/snippets/pom.xml
@@ -34,7 +34,7 @@
com.google.cloud
libraries-bom
- 26.49.0
+ 26.50.0
pom
import
diff --git a/versions.txt b/versions.txt
index d23a72b429..2460651983 100644
--- a/versions.txt
+++ b/versions.txt
@@ -1,11 +1,11 @@
# Format:
# module:released-version:current-version
-google-cloud-firestore:3.28.0:3.28.0
-google-cloud-firestore-admin:3.28.0:3.28.0
-google-cloud-firestore-bom:3.28.0:3.28.0
-grpc-google-cloud-firestore-admin-v1:3.28.0:3.28.0
-grpc-google-cloud-firestore-v1:3.28.0:3.28.0
-proto-google-cloud-firestore-admin-v1:3.28.0:3.28.0
-proto-google-cloud-firestore-v1:3.28.0:3.28.0
-proto-google-cloud-firestore-bundle-v1:3.28.0:3.28.0
+google-cloud-firestore:3.29.0:3.29.0
+google-cloud-firestore-admin:3.29.0:3.29.0
+google-cloud-firestore-bom:3.29.0:3.29.0
+grpc-google-cloud-firestore-admin-v1:3.29.0:3.29.0
+grpc-google-cloud-firestore-v1:3.29.0:3.29.0
+proto-google-cloud-firestore-admin-v1:3.29.0:3.29.0
+proto-google-cloud-firestore-v1:3.29.0:3.29.0
+proto-google-cloud-firestore-bundle-v1:3.29.0:3.29.0