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 60395c3526..c055b559e6 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 @@ -29,6 +29,7 @@ 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; @@ -223,12 +224,12 @@ ApiFuture getFuture() { } void deliverFirstResponse() { - metricsContext.recordFirstResponseLatency(); + metricsContext.recordLatency(MetricType.FIRST_RESPONSE_LATENCY); } void deliverError(Throwable throwable) { future.setException(throwable); - metricsContext.recordEndToEndLatency(throwable); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, throwable); } void deliverResult( @@ -238,7 +239,7 @@ void deliverResult( try { T result = processResult(serverData, readTime, metrics); future.set(result); - metricsContext.recordEndToEndLatency(); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY); } catch (Exception error) { deliverError(error); } diff --git a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/BulkWriter.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/BulkWriter.java index 65cb946d20..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 @@ -28,6 +28,7 @@ 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; @@ -935,10 +936,10 @@ private void sendBatchLocked(final BulkCommitBatch batch, final boolean flush) { }, bulkWriterExecutor); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(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 ee3b9b8fb4..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 @@ -23,6 +23,7 @@ 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; @@ -137,15 +138,15 @@ public ApiFuture> getPartitions(long desiredPartitionCount) }, MoreExecutors.directExecutor()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (ApiException exception) { span.end(exception); - metricsContext.recordEndToEndLatency(exception); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, exception); throw FirestoreException.forApiException(exception); } catch (Throwable throwable) { span.end(throwable); - metricsContext.recordEndToEndLatency(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 a2e8b3f55a..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 @@ -26,6 +26,7 @@ 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; @@ -184,15 +185,15 @@ public void remove() { } }; span.end(); - metricsContext.recordEndToEndLatency(); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY); return result; } catch (ApiException exception) { span.end(exception); - metricsContext.recordEndToEndLatency(exception); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, exception); throw FirestoreException.forApiException(exception); } catch (Throwable throwable) { span.end(throwable); - metricsContext.recordEndToEndLatency(throwable); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, throwable); throw throwable; } } @@ -229,11 +230,11 @@ public ApiFuture add(@Nonnull final Map field ApiFutures.transform( createFuture, writeResult -> documentReference, MoreExecutors.directExecutor()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(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 25f1eadcd4..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 @@ -24,6 +24,7 @@ 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; @@ -165,11 +166,11 @@ public ApiFuture create(@Nonnull Map fields) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.create(this, fields).commit()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -191,11 +192,11 @@ public ApiFuture create(@Nonnull Object pojo) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.create(this, pojo).commit()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -217,11 +218,11 @@ public ApiFuture set(@Nonnull Map fields) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.set(this, fields).commit()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -246,11 +247,11 @@ public ApiFuture set( WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.set(this, fields, options).commit()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -272,11 +273,11 @@ public ApiFuture set(@Nonnull Object pojo) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.set(this, pojo).commit()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -300,11 +301,11 @@ public ApiFuture set(@Nonnull Object pojo, @Nonnull SetOptions opti WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.set(this, pojo, options).commit()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -326,11 +327,11 @@ public ApiFuture update(@Nonnull Map fields) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.update(this, fields).commit()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -354,11 +355,11 @@ public ApiFuture update(@Nonnull Map fields, Precon ApiFuture result = extractFirst(writeBatch.update(this, fields, options).commit()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -384,11 +385,11 @@ public ApiFuture update( ApiFuture result = extractFirst(writeBatch.update(this, field, value, moreFieldsAndValues).commit()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -414,11 +415,11 @@ public ApiFuture update( ApiFuture result = extractFirst(writeBatch.update(this, fieldPath, value, moreFieldsAndValues).commit()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -449,11 +450,11 @@ public ApiFuture update( extractFirst( writeBatch.update(this, options, field, value, moreFieldsAndValues).commit()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -484,11 +485,11 @@ public ApiFuture update( extractFirst( writeBatch.update(this, options, fieldPath, value, moreFieldsAndValues).commit()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -509,11 +510,11 @@ public ApiFuture delete(@Nonnull Precondition options) { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.delete(this, options).commit()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -533,11 +534,11 @@ public ApiFuture delete() { WriteBatch writeBatch = rpcContext.getFirestore().batch(); ApiFuture result = extractFirst(writeBatch.delete(this).commit()); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -558,11 +559,11 @@ public ApiFuture get() { try (Scope ignored = span.makeCurrent()) { ApiFuture result = extractFirst(rpcContext.getFirestore().getAll(this)); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -585,11 +586,11 @@ public ApiFuture get(FieldMask fieldMask) { ApiFuture result = extractFirst(rpcContext.getFirestore().getAll(new DocumentReference[] {this}, fieldMask)); span.endAtFuture(result); - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -641,11 +642,11 @@ public void remove() { } }; span.end(); - metricsContext.recordEndToEndLatency(); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY); return result; } catch (ApiException exception) { span.end(exception); - metricsContext.recordEndToEndLatency(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 93acd29ef4..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 @@ -34,6 +34,7 @@ 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; @@ -266,7 +267,7 @@ public void onResponse(BatchGetDocumentsResponse response) { .addEvent( TelemetryConstants.METHOD_NAME_BATCH_GET_DOCUMENTS + ": First response received"); - metricsContext.recordFirstResponseLatency(); + metricsContext.recordLatency(MetricType.FIRST_RESPONSE_LATENCY); } else if (numResponses % NUM_RESPONSES_PER_TRACE_EVENT == 0) { getTraceUtil() .currentSpan() @@ -311,7 +312,7 @@ public void onResponse(BatchGetDocumentsResponse response) { @Override public void onError(Throwable throwable) { getTraceUtil().currentSpan().end(throwable); - metricsContext.recordEndToEndLatency(throwable); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, throwable); apiStreamObserver.onError(throwable); } @@ -327,7 +328,7 @@ public void onComplete() { + numResponses + " responses.", Collections.singletonMap(ATTRIBUTE_KEY_NUM_RESPONSES, numResponses)); - metricsContext.recordEndToEndLatency(); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY); apiStreamObserver.onCompleted(); } }; @@ -460,9 +461,9 @@ public ApiFuture runAsyncTransaction( // that cannot be tracked client side. result = new ServerSideTransactionRunner<>(this, updateFunction, transactionOptions).run(); } - metricsContext.recordEndToEndLatencyAtFuture(result); + metricsContext.recordLatencyAtFuture(MetricType.END_TO_END_LATENCY, result); } catch (Exception error) { - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } return result; 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 2edd7e71e7..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,7 +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; @@ -60,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 @@ -86,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 @@ -94,6 +102,13 @@ private TraceUtil getTraceUtil() { } ApiFuture 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()); @@ -103,6 +118,7 @@ ApiFuture run() { try (Scope ignored = runTransactionSpan.makeCurrent()) { runTransactionContext = getTraceUtil().currentContext(); --attemptsRemaining; + metricsContext.incrementCounter(); ApiFuture result = ApiFutures.catchingAsync( ApiFutures.transformAsync( @@ -237,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 1f758ab1e6..7564de7266 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 @@ -29,6 +29,7 @@ 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; @@ -170,7 +171,7 @@ public void onCompleted() { return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -264,7 +265,7 @@ public void onCompleted() { return result; } catch (Exception error) { span.end(error); - metricsContext.recordEndToEndLatency(error); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, error); throw error; } } @@ -285,20 +286,20 @@ public MonitoredStreamResponseObserver( public void onNext(RunQueryResponse value) { if (!receivedFirstResponse) { receivedFirstResponse = true; - metricsContext.recordFirstResponseLatency(); + metricsContext.recordLatency(MetricType.FIRST_RESPONSE_LATENCY); } observer.onNext(value); } @Override public void onError(Throwable t) { - metricsContext.recordEndToEndLatency(t); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY, t); observer.onError(t); } @Override public void onCompleted() { - metricsContext.recordEndToEndLatency(); + metricsContext.recordLatency(MetricType.END_TO_END_LATENCY); observer.onCompleted(); } } 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 index c940feb5a9..621efb914c 100644 --- 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 @@ -22,34 +22,44 @@ 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 Meter meter; - private DoubleHistogram endToEndRequestLatency; + 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) { @@ -62,24 +72,15 @@ public BuiltinMetricsProvider(OpenTelemetry openTelemetry) { } } - public ApiTracerFactory getApiTracerFactory() { - return this.apiTracerFactory; - } - - public void endToEndLatencyRecorder(double latency, Map attributes) { - recordLatency(endToEndRequestLatency, latency, attributes); - } - - public void firstResponseLatencyRecorder(double latency, Map attributes) { - recordLatency(firstResponseLatency, latency, attributes); - } - - private void recordLatency( - DoubleHistogram latencyHistogram, double latency, Map attributes) { - if (latencyHistogram != null) { - attributes.putAll(staticAttributes); - latencyHistogram.record(latency, toOtelAttributes(attributes)); + 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. */ @@ -91,9 +92,9 @@ private void configureRPCLayerMetrics() { /** Registers metrics to be collected at the Firestore SDK layer */ private void configureSDKLayerMetrics() { - this.meter = openTelemetry.getMeter(FIRESTORE_METER_NAME); + Meter meter = openTelemetry.getMeter(FIRESTORE_METER_NAME); - this.endToEndRequestLatency = + this.endToEndLatency = meter .histogramBuilder(METRIC_PREFIX + "/" + METRIC_NAME_END_TO_END_LATENCY) .setDescription("Firestore operations' end-to-end latency") @@ -106,18 +107,70 @@ private void configureSDKLayerMetrics() { .setDescription("Firestore streaming operations' first response latency") .setUnit(MILLISECOND_UNIT) .build(); - // TODO(metrics): add transaction latency and retry count metrics + + 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(); } - 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); + 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); } - return staticAttributes; } private Attributes toOtelAttributes(Map attributes) { 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 index 9c7e4d40a2..0642033c00 100644 --- 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 @@ -18,6 +18,7 @@ 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; /** @@ -29,16 +30,19 @@ class DisabledMetricsUtil implements MetricsUtil { class MetricsContext implements MetricsUtil.MetricsContext { @Override - public void recordEndToEndLatencyAtFuture(ApiFuture futureValue) {} + public void recordLatencyAtFuture(MetricType metric, ApiFuture futureValue) {} @Override - public void recordEndToEndLatency() {} + public void recordLatency(MetricType metric) {} @Override - public void recordEndToEndLatency(Throwable t) {} + public void recordLatency(MetricType metric, Throwable t) {} @Override - public void recordFirstResponseLatency() {} + public void recordCounterAtFuture(MetricType metric, ApiFuture futureValue) {} + + @Override + public void incrementCounter() {} } @Override 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 index 5f15204e33..5a9bd4d7b6 100644 --- 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 @@ -32,6 +32,7 @@ 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; @@ -169,68 +170,93 @@ private void addTracerFactory( class MetricsContext implements MetricsUtil.MetricsContext { private final Stopwatch stopwatch; - private final String methodName; + private int counter; + protected final String methodName; public MetricsContext(String methodName) { this.stopwatch = Stopwatch.createStarted(); this.methodName = methodName; + this.counter = 0; } - public void recordEndToEndLatencyAtFuture(ApiFuture futureValue) { + public void recordLatencyAtFuture(MetricType metric, ApiFuture futureValue) { ApiFutures.addCallback( futureValue, new ApiFutureCallback() { @Override public void onFailure(Throwable t) { - recordEndToEndLatency(t); + recordLatency(metric, t); } @Override public void onSuccess(T result) { - recordEndToEndLatency(); + recordLatency(metric); } }, MoreExecutors.directExecutor()); } - public void recordEndToEndLatency() { - recordEndToEndLatency(StatusCode.Code.OK.toString()); + public void recordLatency(MetricType metric) { + recordLatency(metric, StatusCode.Code.OK.toString()); } - public void recordEndToEndLatency(Throwable t) { - recordEndToEndLatency(extractErrorStatus(t)); + public void recordLatency(MetricType metric, Throwable t) { + recordLatency(metric, extractErrorStatus(t)); } - public void recordFirstResponseLatency() { + private void recordLatency(MetricType metric, String status) { double elapsedTime = stopwatch.elapsed(TimeUnit.MILLISECONDS); - Map attributes = createAttributes(StatusCode.Code.OK.toString()); - defaultMetricsProvider.firstResponseLatencyRecorder(elapsedTime, attributes); - customMetricsProvider.firstResponseLatencyRecorder(elapsedTime, attributes); + Map attributes = createAttributes(status, methodName); + defaultMetricsProvider.latencyRecorder(metric, elapsedTime, attributes); + customMetricsProvider.latencyRecorder(metric, elapsedTime, attributes); } - private void recordEndToEndLatency(String status) { - double elapsedTime = stopwatch.elapsed(TimeUnit.MILLISECONDS); - Map attributes = createAttributes(status); - defaultMetricsProvider.endToEndLatencyRecorder(elapsedTime, attributes); - customMetricsProvider.endToEndLatencyRecorder(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 Map createAttributes(String status) { - Map attributes = new HashMap<>(); - attributes.put(METRIC_ATTRIBUTE_KEY_METHOD.getKey(), methodName); - attributes.put(METRIC_ATTRIBUTE_KEY_STATUS.getKey(), status); - return attributes; + 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(); } - 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(); + 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/MetricsUtil.java b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java index c43c2db3af..02ac3a26be 100644 --- a/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java +++ b/google-cloud-firestore/src/main/java/com/google/cloud/firestore/telemetry/MetricsUtil.java @@ -20,6 +20,7 @@ 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; @@ -91,23 +92,28 @@ static boolean shouldCreateEnabledInstance() { */ abstract void addMetricsTracerFactory(List apiTracerFactories); - /** A context for recording metrics. */ + /** 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 recordEndToEndLatencyAtFuture(ApiFuture futureValue); + void recordLatencyAtFuture(MetricType metric, ApiFuture futureValue); + + /** Records specific type of latency for the current operation. */ + void recordLatency(MetricType metric); - /** Records end-to-end latency for the current operation. */ - void recordEndToEndLatency(); + /** Records specific type of latency for the current operation, which ended with a throwable. */ + void recordLatency(MetricType metric, Throwable t); - /** Records end-to-end latency for the current operation, which ended with a throwable. */ - void recordEndToEndLatency(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); - /** Records first response latency for the current operation. */ - void recordFirstResponseLatency(); + /** 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 index b9e69d8e13..d2ab8bf173 100644 --- 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 @@ -114,4 +114,11 @@ public interface TelemetryConstants { 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 + } }