Skip to content

Commit 3c61b14

Browse files
authored
feat: Introduce interfaces for metrics instrumentation (#2403)
This PR introduces new public interfaces and refactors existing interfaces for metrics instrumentation. All the interfaces are framework-agnostic. Implementation of the interfaces should use an observability framework like OpenTelemetry.
1 parent f969910 commit 3c61b14

File tree

8 files changed

+287
-19
lines changed

8 files changed

+287
-19
lines changed

gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcCallContext.java

+3-1
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,9 @@ public final class GrpcCallContext implements ApiCallContext {
7777
private static final GrpcStatusCode UNAUTHENTICATED_STATUS_CODE =
7878
GrpcStatusCode.of(Status.Code.UNAUTHENTICATED);
7979

80-
static final CallOptions.Key<ApiTracer> TRACER_KEY = CallOptions.Key.create("gax.tracer");
80+
// This field is made public for handwritten libraries to easily access the tracer from
81+
// CallOptions
82+
public static final CallOptions.Key<ApiTracer> TRACER_KEY = CallOptions.Key.create("gax.tracer");
8183

8284
private final Channel channel;
8385
@Nullable private final Credentials credentials;

gax-java/gax/src/main/java/com/google/api/gax/tracing/ApiTracer.java

+21-17
Original file line numberDiff line numberDiff line change
@@ -49,34 +49,38 @@ public interface ApiTracer {
4949
* between clients using gax and external resources to share the same implementation of the
5050
* tracing. For example OpenCensus will install a thread local that can read by the GRPC.
5151
*/
52-
Scope inScope();
52+
default Scope inScope() {
53+
return () -> {
54+
// noop
55+
};
56+
};
5357

5458
/**
5559
* Signals that the overall operation has finished successfully. The tracer is now considered
5660
* closed and should no longer be used.
5761
*/
58-
void operationSucceeded();
62+
default void operationSucceeded() {};
5963

6064
/**
6165
* Signals that the operation was cancelled by the user. The tracer is now considered closed and
6266
* should no longer be used.
6367
*/
64-
void operationCancelled();
68+
default void operationCancelled() {};
6569

6670
/**
6771
* Signals that the overall operation has failed and no further attempts will be made. The tracer
6872
* is now considered closed and should no longer be used.
6973
*
7074
* @param error the final error that caused the operation to fail.
7175
*/
72-
void operationFailed(Throwable error);
76+
default void operationFailed(Throwable error) {};
7377

7478
/**
7579
* Annotates the operation with selected connection id from the {@code ChannelPool}.
7680
*
7781
* @param id the local connection identifier of the selected connection.
7882
*/
79-
void connectionSelected(String id);
83+
default void connectionSelected(String id) {};
8084

8185
/**
8286
* Adds an annotation that an attempt is about to start. In general this should occur at the very
@@ -86,7 +90,7 @@ public interface ApiTracer {
8690
* @deprecated Please use {@link #attemptStarted(Object, int)} instead.
8791
*/
8892
@Deprecated
89-
void attemptStarted(int attemptNumber);
93+
default void attemptStarted(int attemptNumber) {};
9094

9195
/**
9296
* Adds an annotation that an attempt is about to start with additional information from the
@@ -96,64 +100,64 @@ public interface ApiTracer {
96100
* @param attemptNumber the zero based sequential attempt number.
97101
* @param request request of this attempt.
98102
*/
99-
void attemptStarted(Object request, int attemptNumber);
103+
default void attemptStarted(Object request, int attemptNumber) {};
100104

101105
/** Adds an annotation that the attempt succeeded. */
102-
void attemptSucceeded();
106+
default void attemptSucceeded() {};
103107

104108
/** Add an annotation that the attempt was cancelled by the user. */
105-
void attemptCancelled();
109+
default void attemptCancelled() {};
106110

107111
/**
108112
* Adds an annotation that the attempt failed, but another attempt will be made after the delay.
109113
*
110114
* @param error the transient error that caused the attempt to fail.
111115
* @param delay the amount of time to wait before the next attempt will start.
112116
*/
113-
void attemptFailed(Throwable error, Duration delay);
117+
default void attemptFailed(Throwable error, Duration delay) {};
114118

115119
/**
116120
* Adds an annotation that the attempt failed and that no further attempts will be made because
117121
* retry limits have been reached.
118122
*
119123
* @param error the last error received before retries were exhausted.
120124
*/
121-
void attemptFailedRetriesExhausted(Throwable error);
125+
default void attemptFailedRetriesExhausted(Throwable error) {};
122126

123127
/**
124128
* Adds an annotation that the attempt failed and that no further attempts will be made because
125129
* the last error was not retryable.
126130
*
127131
* @param error the error that caused the final attempt to fail.
128132
*/
129-
void attemptPermanentFailure(Throwable error);
133+
default void attemptPermanentFailure(Throwable error) {};
130134

131135
/**
132136
* Signals that the initial RPC for the long running operation failed.
133137
*
134138
* @param error the error that caused the long running operation fail.
135139
*/
136-
void lroStartFailed(Throwable error);
140+
default void lroStartFailed(Throwable error) {};
137141

138142
/**
139143
* Signals that the initial RPC successfully started the long running operation. The long running
140144
* operation will now be polled for completion.
141145
*/
142-
void lroStartSucceeded();
146+
default void lroStartSucceeded() {};
143147

144148
/** Adds an annotation that a streaming response has been received. */
145-
void responseReceived();
149+
default void responseReceived() {};
146150

147151
/** Adds an annotation that a streaming request has been sent. */
148-
void requestSent();
152+
default void requestSent() {};
149153

150154
/**
151155
* Adds an annotation that a batch of writes has been flushed.
152156
*
153157
* @param elementCount the number of elements in the batch.
154158
* @param requestSize the size of the batch in bytes.
155159
*/
156-
void batchRequestSent(long elementCount, long requestSize);
160+
default void batchRequestSent(long elementCount, long requestSize) {};
157161

158162
/**
159163
* A context class to be used with {@link #inScope()} and a try-with-resources block. Closing a

gax-java/gax/src/main/java/com/google/api/gax/tracing/BaseApiTracer.java

+4-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,10 @@
3333
import org.threeten.bp.Duration;
3434

3535
/**
36-
* A base implementation of {@link ApiTracer} that does nothing.
36+
* A base implementation of {@link ApiTracer} that does nothing. With the deprecation of Java 7
37+
* support, all the methods in {@link ApiTracer} are now made default, we no longer need a base
38+
* class that does nothing. This class should be removed once all the references to it are removed
39+
* in Google Cloud Client Libraries.
3740
*
3841
* <p>For internal use only.
3942
*/
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Copyright 2024 Google LLC
3+
*
4+
* Redistribution and use in source and binary forms, with or without
5+
* modification, are permitted provided that the following conditions are
6+
* met:
7+
*
8+
* * Redistributions of source code must retain the above copyright
9+
* notice, this list of conditions and the following disclaimer.
10+
* * Redistributions in binary form must reproduce the above
11+
* copyright notice, this list of conditions and the following disclaimer
12+
* in the documentation and/or other materials provided with the
13+
* distribution.
14+
* * Neither the name of Google LLC nor the names of its
15+
* contributors may be used to endorse or promote products derived from
16+
* this software without specific prior written permission.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22+
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29+
*/
30+
package com.google.api.gax.tracing;
31+
32+
import com.google.api.core.BetaApi;
33+
import com.google.api.core.InternalApi;
34+
import com.google.api.gax.rpc.StubSettings;
35+
import com.google.auto.value.AutoValue;
36+
37+
/** A value class to represent the name of the RPC method in an {@link ApiTracer}. */
38+
@BetaApi
39+
@InternalApi
40+
@AutoValue
41+
public abstract class MethodName {
42+
/**
43+
* Creates a new instance of the RPC method name.
44+
*
45+
* @param serviceName The name of the service. In general this will be GAPIC generated service
46+
* name {@link StubSettings#getServiceName()}. However, in some cases, when the GAPIC
47+
* generated service is wrapped, this will be overridden to specify the manually written
48+
* wrapper's name.
49+
* @param methodName The name of the logical operation being traced.
50+
*/
51+
public static MethodName of(String serviceName, String methodName) {
52+
return new AutoValue_MethodName(serviceName, methodName);
53+
}
54+
55+
/** The name of the service. ie BigtableData */
56+
public abstract String getServiceName();
57+
58+
/** The name of the logical operation being traced. ie. ReadRows. */
59+
public abstract String getMethodName();
60+
61+
@Override
62+
public String toString() {
63+
return getServiceName() + "." + getMethodName();
64+
}
65+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright 2024 Google LLC
3+
*
4+
* Redistribution and use in source and binary forms, with or without
5+
* modification, are permitted provided that the following conditions are
6+
* met:
7+
*
8+
* * Redistributions of source code must retain the above copyright
9+
* notice, this list of conditions and the following disclaimer.
10+
* * Redistributions in binary form must reproduce the above
11+
* copyright notice, this list of conditions and the following disclaimer
12+
* in the documentation and/or other materials provided with the
13+
* distribution.
14+
* * Neither the name of Google LLC nor the names of its
15+
* contributors may be used to endorse or promote products derived from
16+
* this software without specific prior written permission.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22+
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29+
*/
30+
31+
package com.google.api.gax.tracing;
32+
33+
import com.google.api.core.BetaApi;
34+
import com.google.api.core.InternalApi;
35+
import java.util.Map;
36+
37+
/**
38+
* Provides an interface for metrics recording. The implementer is expected to use an observability
39+
* framework, e.g. OpenTelemetry. There should be only one instance of MetricsRecorder per client,
40+
* all the methods in this class are expected to be called from multiple threads, hence the
41+
* implementation must be thread safe.
42+
*/
43+
@BetaApi
44+
@InternalApi
45+
public interface MetricsRecorder {
46+
47+
/** Records the latency of an RPC attempt */
48+
default void recordAttemptLatency(double attemptLatency, Map<String, String> attributes) {}
49+
50+
/** Records the count of RPC attempts */
51+
default void recordAttemptCount(long count, Map<String, String> attributes) {}
52+
53+
/**
54+
* Records the total end-to-end latency for an operation, including the initial RPC attempts and
55+
* subsequent retries.
56+
*/
57+
default void recordOperationLatency(double operationLatency, Map<String, String> attributes) {}
58+
59+
/** Records the count of operations */
60+
default void recordOperationCount(long count, Map<String, String> attributes) {}
61+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright 2024 Google LLC
3+
*
4+
* Redistribution and use in source and binary forms, with or without
5+
* modification, are permitted provided that the following conditions are
6+
* met:
7+
*
8+
* * Redistributions of source code must retain the above copyright
9+
* notice, this list of conditions and the following disclaimer.
10+
* * Redistributions in binary form must reproduce the above
11+
* copyright notice, this list of conditions and the following disclaimer
12+
* in the documentation and/or other materials provided with the
13+
* distribution.
14+
* * Neither the name of Google LLC nor the names of its
15+
* contributors may be used to endorse or promote products derived from
16+
* this software without specific prior written permission.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22+
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29+
*/
30+
31+
package com.google.api.gax.tracing;
32+
33+
import com.google.api.core.BetaApi;
34+
import com.google.api.core.InternalApi;
35+
36+
/**
37+
* This class computes generic metrics that can be observed in the lifecycle of an RPC operation.
38+
* The responsibility of recording metrics should delegate to {@link MetricsRecorder}, hence this
39+
* class should not have any knowledge about the observability framework used for metrics recording.
40+
*/
41+
@BetaApi
42+
@InternalApi
43+
public class MetricsTracer implements ApiTracer {
44+
45+
public MetricsTracer(MethodName methodName, MetricsRecorder metricsRecorder) {}
46+
47+
/**
48+
* Add attributes that will be attached to all metrics. This is expected to be called by
49+
* handwritten client teams to add additional attributes that are not supposed be collected by
50+
* Gax.
51+
*/
52+
public void addAttributes(String key, String value) {};
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/*
2+
* Copyright 2024 Google LLC
3+
*
4+
* Redistribution and use in source and binary forms, with or without
5+
* modification, are permitted provided that the following conditions are
6+
* met:
7+
*
8+
* * Redistributions of source code must retain the above copyright
9+
* notice, this list of conditions and the following disclaimer.
10+
* * Redistributions in binary form must reproduce the above
11+
* copyright notice, this list of conditions and the following disclaimer
12+
* in the documentation and/or other materials provided with the
13+
* distribution.
14+
* * Neither the name of Google LLC nor the names of its
15+
* contributors may be used to endorse or promote products derived from
16+
* this software without specific prior written permission.
17+
*
18+
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
19+
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
20+
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
21+
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
22+
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
23+
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
24+
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25+
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
26+
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27+
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28+
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29+
*/
30+
package com.google.api.gax.tracing;
31+
32+
import com.google.api.core.BetaApi;
33+
import com.google.api.core.InternalApi;
34+
35+
/**
36+
* A {@link ApiTracerFactory} to build instances of {@link MetricsTracer}.
37+
*
38+
* <p>This class wraps the {@link MetricsRecorder} and pass it to {@link MetricsTracer}. It will be
39+
* used to record metrics in {@link MetricsTracer}.
40+
*
41+
* <p>This class is expected to be initialized once during client initialization.
42+
*/
43+
@BetaApi
44+
@InternalApi
45+
public class MetricsTracerFactory implements ApiTracerFactory {
46+
protected MetricsRecorder metricsRecorder;
47+
48+
public MetricsTracerFactory(MetricsRecorder metricsRecorder) {
49+
this.metricsRecorder = metricsRecorder;
50+
}
51+
52+
@Override
53+
public ApiTracer newTracer(ApiTracer parent, SpanName spanName, OperationType operationType) {
54+
return new MetricsTracer(
55+
MethodName.of(spanName.getClientName(), spanName.getMethodName()), metricsRecorder);
56+
}
57+
}

0 commit comments

Comments
 (0)