Skip to content

Commit

Permalink
feat: Implement OpentelemetryMetricsRecorder
Browse files Browse the repository at this point in the history
  • Loading branch information
lqiu96 committed Feb 26, 2024
1 parent 7902a41 commit 21407aa
Show file tree
Hide file tree
Showing 14 changed files with 1,097 additions and 15 deletions.
8 changes: 2 additions & 6 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
# How to Contribute

We'd love to accept your patches and contributions to this project. There are just a few small guidelines you need to follow before opening an issue or a PR:
1. Ensure the issue was not already reported.
2. Open a new issue if you are unable to find an existing issue addressing your problem. Make sure to include a title and clear description, as much relevant information as possible, and a code sample or an executable test case demonstrating the expected behavior that is not occurring.
3. Discuss the priority and potential solutions with the maintainers in the issue. The maintainers would review the issue and add a label "Accepting Contributions" once the issue is ready for accepting contributions.
4. Open a PR only if the issue is labeled with "Accepting Contributions", ensure the PR description clearly describes the problem and solution. Note that an open PR without an issue labeled with "Accepting Contributions" will not be accepted.

We'd love to accept your patches and contributions to this project. There are
just a few small guidelines you need to follow.

## Contributor License Agreement

Expand Down
1 change: 1 addition & 0 deletions gax-java/dependencies.properties
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ maven.com_google_api_grpc_proto_google_common_protos=com.google.api.grpc:proto-g
maven.com_google_api_grpc_grpc_google_common_protos=com.google.api.grpc:grpc-google-common-protos:2.33.0
maven.com_google_auth_google_auth_library_oauth2_http=com.google.auth:google-auth-library-oauth2-http:1.23.0
maven.com_google_auth_google_auth_library_credentials=com.google.auth:google-auth-library-credentials:1.23.0
maven.io_opentelemetry_opentelemetry_api=io.opentelemetry:opentelemetry-api:1.34.1
maven.io_opencensus_opencensus_api=io.opencensus:opencensus-api:0.31.1
maven.io_opencensus_opencensus_contrib_grpc_metrics=io.opencensus:opencensus-contrib-grpc-metrics:0.31.1
maven.io_opencensus_opencensus_contrib_http_util=io.opencensus:opencensus-contrib-http-util:0.31.1
Expand Down
1 change: 1 addition & 0 deletions gax-java/gax/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ _COMPILE_DEPS = [
"@com_google_code_findbugs_jsr305//jar",
"@com_google_errorprone_error_prone_annotations//jar",
"@com_google_guava_guava//jar",
"@io_opentelemetry_opentelemetry_api//jar",
"@io_opencensus_opencensus_api//jar",
"@io_opencensus_opencensus_contrib_http_util//jar",
"@io_grpc_grpc_java//context:context",
Expand Down
12 changes: 12 additions & 0 deletions gax-java/gax/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,18 @@
<artifactId>graal-sdk</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-api</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-sdk</artifactId>
</dependency>
<dependency>
<groupId>io.opentelemetry</groupId>
<artifactId>opentelemetry-exporter-otlp</artifactId>
</dependency>
</dependencies>

<build>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
import com.google.api.core.ApiFunction;
import com.google.api.gax.core.CredentialsProvider;
import com.google.api.gax.core.ExecutorProvider;
import com.google.api.gax.tracing.ApiTracerFactory;
import com.google.common.base.MoreObjects;
import java.io.IOException;
import java.util.concurrent.Executor;
Expand Down Expand Up @@ -284,6 +285,16 @@ public B setGdchApiAudience(@Nullable String gdchApiAudience) {
return self();
}

/**
* Sets the ApiTracerFactory for the client instance. To enable default metrics, users need to
* create an instance of metricsRecorder and pass it to the metricsTracerFactory, and set it
* here.
*/
public B setTracerFactory(@Nullable ApiTracerFactory tracerFactory) {
stubSettings.setTracerFactory(tracerFactory);
return self();
}

/**
* Gets the ExecutorProvider that was previously set on this Builder. This ExecutorProvider is
* to use for running asynchronous API call logic (such as retries and long-running operations),
Expand Down Expand Up @@ -351,6 +362,11 @@ public Duration getWatchdogCheckInterval() {
return stubSettings.getStreamWatchdogCheckInterval();
}

/** Gets the TracerFactory that was previously set in this Builder */
public ApiTracerFactory getTracerFactory() {
return stubSettings.getTracerFactory();
}

/** Gets the GDCH API audience that was previously set in this Builder */
@Nullable
public String getGdchApiAudience() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,12 +47,14 @@
* This class computes generic metrics that can be observed in the lifecycle of an RPC operation.
* The responsibility of recording metrics should delegate to {@link MetricsRecorder}, hence this
* class should not have any knowledge about the observability framework used for metrics recording.
* method_name and language will be autopopulated attributes. Default value of language is 'Java'.
*/
@BetaApi
@InternalApi
public class MetricsTracer implements ApiTracer {

private static final String STATUS_ATTRIBUTE = "status";
private static final String LANGUAGE = "Java";

private Stopwatch attemptTimer;

Expand All @@ -64,6 +66,7 @@ public class MetricsTracer implements ApiTracer {

public MetricsTracer(MethodName methodName, MetricsRecorder metricsRecorder) {
this.attributes.put("method_name", methodName.toString());
this.attributes.put("language", LANGUAGE);
this.metricsRecorder = metricsRecorder;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/*
* Copyright 2024 Google LLC
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
* * Neither the name of Google LLC nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

package com.google.api.gax.tracing;

import com.google.common.annotations.VisibleForTesting;
import io.opentelemetry.api.common.Attributes;
import io.opentelemetry.api.common.AttributesBuilder;
import io.opentelemetry.api.metrics.DoubleHistogram;
import io.opentelemetry.api.metrics.LongCounter;
import io.opentelemetry.api.metrics.Meter;
import java.util.Map;

public class OpentelemetryMetricsRecorder implements MetricsRecorder {

private Meter meter;

private DoubleHistogram attemptLatencyRecorder;

private DoubleHistogram operationLatencyRecorder;

private LongCounter operationCountRecorder;

private LongCounter attemptCountRecorder;

public OpentelemetryMetricsRecorder(Meter meter) {
this.meter = meter;
this.attemptLatencyRecorder =
meter
.histogramBuilder("attempt_latency")
.setDescription("Duration of an individual attempt")
.setUnit("ms")
.build();
this.operationLatencyRecorder =
meter
.histogramBuilder("operation_latency")
.setDescription(
"Total time until final operation success or failure, including retries and backoff.")
.setUnit("ms")
.build();
this.operationCountRecorder =
meter
.counterBuilder("operation_count")
.setDescription("Count of Operations")
.setUnit("1")
.build();
this.attemptCountRecorder =
meter
.counterBuilder("attempt_count")
.setDescription("Count of Attempts")
.setUnit("1")
.build();
}

public void recordAttemptLatency(double attemptLatency, Map<String, String> attributes) {
attemptLatencyRecorder.record(attemptLatency, toOtelAttributes(attributes));
}

public void recordAttemptCount(long count, Map<String, String> attributes) {
attemptCountRecorder.add(count, toOtelAttributes(attributes));
}

public void recordOperationLatency(double operationLatency, Map<String, String> attributes) {
operationLatencyRecorder.record(operationLatency, toOtelAttributes(attributes));
}

public void recordOperationCount(long count, Map<String, String> attributes) {
operationCountRecorder.add(count, toOtelAttributes(attributes));
}

@VisibleForTesting
Attributes toOtelAttributes(Map<String, String> attributes) {

if (attributes == null) {
throw new IllegalArgumentException("Input attributes map cannot be null");
}

AttributesBuilder attributesBuilder = Attributes.builder();
attributes.forEach(attributesBuilder::put);
return attributesBuilder.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ public void testOperationSucceeded_recordsAttributes() {
Map<String, String> attributes =
ImmutableMap.of(
"status", "OK",
"method_name", "fake_service.fake_method");
"method_name", "fake_service.fake_method",
"language", "Java");

verify(metricsRecorder).recordOperationCount(1, attributes);
verify(metricsRecorder).recordOperationLatency(anyDouble(), eq(attributes));
Expand All @@ -96,7 +97,8 @@ public void testOperationFailed_recordsAttributes() {
Map<String, String> attributes =
ImmutableMap.of(
"status", "INVALID_ARGUMENT",
"method_name", "fake_service.fake_method");
"method_name", "fake_service.fake_method",
"language", "Java");

verify(metricsRecorder).recordOperationCount(1, attributes);
verify(metricsRecorder).recordOperationLatency(anyDouble(), eq(attributes));
Expand All @@ -112,7 +114,8 @@ public void testOperationCancelled_recordsAttributes() {
Map<String, String> attributes =
ImmutableMap.of(
"status", "CANCELLED",
"method_name", "fake_service.fake_method");
"method_name", "fake_service.fake_method",
"language", "Java");

verify(metricsRecorder).recordOperationCount(1, attributes);
verify(metricsRecorder).recordOperationLatency(anyDouble(), eq(attributes));
Expand All @@ -132,7 +135,8 @@ public void testAttemptSucceeded_recordsAttributes() {
Map<String, String> attributes =
ImmutableMap.of(
"status", "OK",
"method_name", "fake_service.fake_method");
"method_name", "fake_service.fake_method",
"language", "Java");

verify(metricsRecorder).recordAttemptCount(1, attributes);
verify(metricsRecorder).recordAttemptLatency(anyDouble(), eq(attributes));
Expand All @@ -155,7 +159,8 @@ public void testAttemptFailed_recordsAttributes() {
Map<String, String> attributes =
ImmutableMap.of(
"status", "INVALID_ARGUMENT",
"method_name", "fake_service.fake_method");
"method_name", "fake_service.fake_method",
"language", "Java");

verify(metricsRecorder).recordAttemptCount(1, attributes);
verify(metricsRecorder).recordAttemptLatency(anyDouble(), eq(attributes));
Expand All @@ -174,7 +179,8 @@ public void testAttemptCancelled_recordsAttributes() {
Map<String, String> attributes =
ImmutableMap.of(
"status", "CANCELLED",
"method_name", "fake_service.fake_method");
"method_name", "fake_service.fake_method",
"language", "Java");

verify(metricsRecorder).recordAttemptCount(1, attributes);
verify(metricsRecorder).recordAttemptLatency(anyDouble(), eq(attributes));
Expand All @@ -196,7 +202,8 @@ public void testAttemptFailedRetriesExhausted_recordsAttributes() {
Map<String, String> attributes =
ImmutableMap.of(
"status", "DEADLINE_EXCEEDED",
"method_name", "fake_service.fake_method");
"method_name", "fake_service.fake_method",
"language", "Java");

verify(metricsRecorder).recordAttemptCount(1, attributes);
verify(metricsRecorder).recordAttemptLatency(anyDouble(), eq(attributes));
Expand All @@ -218,7 +225,8 @@ public void testAttemptPermanentFailure_recordsAttributes() {
Map<String, String> attributes =
ImmutableMap.of(
"status", "NOT_FOUND",
"method_name", "fake_service.fake_method");
"method_name", "fake_service.fake_method",
"language", "Java");

verify(metricsRecorder).recordAttemptCount(1, attributes);
verify(metricsRecorder).recordAttemptLatency(anyDouble(), eq(attributes));
Expand Down
Loading

0 comments on commit 21407aa

Please sign in to comment.