Skip to content

Commit 3593c30

Browse files
feat(o11y): Introduce rpc.system.name and rpc.method in gRPC (#4121)
This PR introduces the `rpc.system.name` and `rpc.method` span attributes in gRPC. To achieve this, all `Traced*Callable` classes were modified with a constructor overload that accepts an `ApiTracerContext` instance. Such an instance will be preferred over `SpanName` when deciding which (also newly introduced) overload of `ApiTracerFactory.newTracer()` will be called. The new method in `ApiTracerFactory`, `newTracer(ApiTracer parent, ApiTracerContext tracerContext, OperationType operationType)` will by default merge the existing factory's context with the new one and simply create a new `SpanName`. However, the logic in `SpanTracerFactory` will use the context to create the span name directly. The `rpc.system.name` and `rpc.method` properties are obtained from `GrpcCallableFactory` and stored in `ApiTracerContext`, which now holds the regexes for parsing client and method names (used when building `SpanName`s). --------- Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
1 parent 1b10e02 commit 3593c30

File tree

24 files changed

+997
-198
lines changed

24 files changed

+997
-198
lines changed

sdk-platform-java/gax-java/gax-grpc/src/main/java/com/google/api/gax/grpc/GrpcCallableFactory.java

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -29,13 +29,13 @@
2929
*/
3030
package com.google.api.gax.grpc;
3131

32-
import com.google.api.core.InternalApi;
3332
import com.google.api.gax.longrunning.OperationSnapshot;
3433
import com.google.api.gax.rpc.BatchingCallSettings;
3534
import com.google.api.gax.rpc.BidiStreamingCallable;
3635
import com.google.api.gax.rpc.Callables;
3736
import com.google.api.gax.rpc.ClientContext;
3837
import com.google.api.gax.rpc.ClientStreamingCallable;
38+
import com.google.api.gax.rpc.LibraryMetadata;
3939
import com.google.api.gax.rpc.LongRunningClient;
4040
import com.google.api.gax.rpc.OperationCallSettings;
4141
import com.google.api.gax.rpc.OperationCallable;
@@ -46,6 +46,7 @@
4646
import com.google.api.gax.rpc.StreamingCallSettings;
4747
import com.google.api.gax.rpc.UnaryCallSettings;
4848
import com.google.api.gax.rpc.UnaryCallable;
49+
import com.google.api.gax.tracing.ApiTracerContext;
4950
import com.google.api.gax.tracing.SpanName;
5051
import com.google.api.gax.tracing.TracedBatchingCallable;
5152
import com.google.api.gax.tracing.TracedBidiCallable;
@@ -54,19 +55,15 @@
5455
import com.google.api.gax.tracing.TracedOperationInitialCallable;
5556
import com.google.api.gax.tracing.TracedServerStreamingCallable;
5657
import com.google.api.gax.tracing.TracedUnaryCallable;
57-
import com.google.common.base.Preconditions;
58+
import com.google.common.annotations.VisibleForTesting;
5859
import com.google.common.collect.ImmutableSet;
5960
import com.google.longrunning.Operation;
6061
import com.google.longrunning.stub.OperationsStub;
6162
import io.grpc.MethodDescriptor;
62-
import java.util.regex.Matcher;
63-
import java.util.regex.Pattern;
6463
import javax.annotation.Nonnull;
6564

6665
/** Class with utility methods to create grpc-based direct callables. */
6766
public class GrpcCallableFactory {
68-
// Used to extract service and method name from a grpc MethodDescriptor.
69-
private static final Pattern FULL_METHOD_NAME_REGEX = Pattern.compile("^.*?([^./]+)/([^./]+)$");
7067

7168
private GrpcCallableFactory() {}
7269

@@ -112,7 +109,7 @@ public static <RequestT, ResponseT> UnaryCallable<RequestT, ResponseT> createUna
112109
new TracedUnaryCallable<>(
113110
callable,
114111
clientContext.getTracerFactory(),
115-
getSpanName(grpcCallSettings.getMethodDescriptor()));
112+
getApiTracerContext(grpcCallSettings.getMethodDescriptor()));
116113

117114
return callable.withDefaultCallContext(clientContext.getDefaultCallContext());
118115
}
@@ -161,7 +158,7 @@ public static <RequestT, ResponseT> UnaryCallable<RequestT, ResponseT> createBat
161158
new TracedBatchingCallable<>(
162159
baseCallable,
163160
clientContext.getTracerFactory(),
164-
getSpanName(grpcCallSettings.getMethodDescriptor()),
161+
getApiTracerContext(grpcCallSettings.getMethodDescriptor()),
165162
batchingCallSettings.getBatchingDescriptor());
166163

167164
UnaryCallable<RequestT, ResponseT> batchingCallable =
@@ -187,7 +184,8 @@ OperationCallable<RequestT, ResponseT, MetadataT> createOperationCallable(
187184
ClientContext clientContext,
188185
OperationsStub operationsStub) {
189186

190-
SpanName initialSpanName = getSpanName(grpcCallSettings.getMethodDescriptor());
187+
ApiTracerContext tracerContext = getApiTracerContext(grpcCallSettings.getMethodDescriptor());
188+
SpanName initialSpanName = SpanName.of(tracerContext);
191189
SpanName operationSpanName =
192190
SpanName.of(initialSpanName.getClientName(), initialSpanName.getMethodName() + "Operation");
193191

@@ -237,7 +235,7 @@ BidiStreamingCallable<RequestT, ResponseT> createBidiStreamingCallable(
237235
new TracedBidiCallable<>(
238236
callable,
239237
clientContext.getTracerFactory(),
240-
getSpanName(grpcCallSettings.getMethodDescriptor()));
238+
getApiTracerContext(grpcCallSettings.getMethodDescriptor()));
241239

242240
return callable.withDefaultCallContext(clientContext.getDefaultCallContext());
243241
}
@@ -295,7 +293,7 @@ ServerStreamingCallable<RequestT, ResponseT> createServerStreamingCallable(
295293
new TracedServerStreamingCallable<>(
296294
callable,
297295
clientContext.getTracerFactory(),
298-
getSpanName(grpcCallSettings.getMethodDescriptor()));
296+
getApiTracerContext(grpcCallSettings.getMethodDescriptor()));
299297

300298
return callable.withDefaultCallContext(clientContext.getDefaultCallContext());
301299
}
@@ -323,16 +321,17 @@ ClientStreamingCallable<RequestT, ResponseT> createClientStreamingCallable(
323321
new TracedClientStreamingCallable<>(
324322
callable,
325323
clientContext.getTracerFactory(),
326-
getSpanName(grpcCallSettings.getMethodDescriptor()));
324+
getApiTracerContext(grpcCallSettings.getMethodDescriptor()));
327325

328326
return callable.withDefaultCallContext(clientContext.getDefaultCallContext());
329327
}
330328

331-
@InternalApi("Visible for testing")
332-
static SpanName getSpanName(@Nonnull MethodDescriptor<?, ?> methodDescriptor) {
333-
Matcher matcher = FULL_METHOD_NAME_REGEX.matcher(methodDescriptor.getFullMethodName());
334-
335-
Preconditions.checkArgument(matcher.matches(), "Invalid fullMethodName");
336-
return SpanName.of(matcher.group(1), matcher.group(2));
329+
@VisibleForTesting
330+
static ApiTracerContext getApiTracerContext(@Nonnull MethodDescriptor<?, ?> methodDescriptor) {
331+
return ApiTracerContext.newBuilder()
332+
.setFullMethodName(methodDescriptor.getFullMethodName())
333+
.setTransport(ApiTracerContext.Transport.GRPC)
334+
.setLibraryMetadata(LibraryMetadata.empty())
335+
.build();
337336
}
338337
}

sdk-platform-java/gax-java/gax-grpc/src/test/java/com/google/api/gax/grpc/GrpcCallableFactoryTest.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,7 @@ void testGetSpanName() {
157157
.setResponseMarshaller(Mockito.mock(Marshaller.class))
158158
.build();
159159

160-
SpanName actualSpanName = GrpcCallableFactory.getSpanName(descriptor);
160+
SpanName actualSpanName = SpanName.of(GrpcCallableFactory.getApiTracerContext(descriptor));
161161
assertThat(actualSpanName).isEqualTo(SpanName.of("Bigtable", "ReadRows"));
162162
}
163163

@@ -172,7 +172,7 @@ void testGetSpanNameUnqualified() {
172172
.setResponseMarshaller(Mockito.mock(Marshaller.class))
173173
.build();
174174

175-
SpanName actualSpanName = GrpcCallableFactory.getSpanName(descriptor);
175+
SpanName actualSpanName = SpanName.of(GrpcCallableFactory.getApiTracerContext(descriptor));
176176
assertThat(actualSpanName).isEqualTo(SpanName.of("UnqualifiedService", "ReadRows"));
177177
}
178178

@@ -192,7 +192,7 @@ void testGetSpanNameInvalid() {
192192

193193
IllegalArgumentException actualError = null;
194194
try {
195-
SpanName spanName = GrpcCallableFactory.getSpanName(descriptor);
195+
SpanName spanName = SpanName.of(GrpcCallableFactory.getApiTracerContext(descriptor));
196196
Truth.assertWithMessage(
197197
"Invalid method descriptor should not have a valid span name: %s should not generate the spanName: %s",
198198
invalidName, spanName)

sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonCallableFactory.java

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import com.google.api.gax.rpc.BatchingCallSettings;
3636
import com.google.api.gax.rpc.Callables;
3737
import com.google.api.gax.rpc.ClientContext;
38+
import com.google.api.gax.rpc.LibraryMetadata;
3839
import com.google.api.gax.rpc.LongRunningClient;
3940
import com.google.api.gax.rpc.OperationCallSettings;
4041
import com.google.api.gax.rpc.OperationCallable;
@@ -43,17 +44,13 @@
4344
import com.google.api.gax.rpc.ServerStreamingCallable;
4445
import com.google.api.gax.rpc.UnaryCallSettings;
4546
import com.google.api.gax.rpc.UnaryCallable;
47+
import com.google.api.gax.tracing.ApiTracerContext;
4648
import com.google.api.gax.tracing.SpanName;
4749
import com.google.api.gax.tracing.TracedUnaryCallable;
48-
import com.google.common.base.Preconditions;
49-
import java.util.regex.Matcher;
50-
import java.util.regex.Pattern;
5150
import javax.annotation.Nonnull;
5251

5352
/** Class with utility methods to create http/json-based direct callables. */
5453
public class HttpJsonCallableFactory {
55-
// Used to extract service and method name from a HttpJson MethodDescriptor.
56-
private static final Pattern FULL_METHOD_NAME_REGEX = Pattern.compile("^.*?([^./]+)/([^./]+)$");
5754

5855
private HttpJsonCallableFactory() {}
5956

@@ -226,9 +223,12 @@ ServerStreamingCallable<RequestT, ResponseT> createServerStreamingCallable(
226223

227224
@InternalApi("Visible for testing")
228225
static SpanName getSpanName(@Nonnull ApiMethodDescriptor<?, ?> methodDescriptor) {
229-
Matcher matcher = FULL_METHOD_NAME_REGEX.matcher(methodDescriptor.getFullMethodName());
230-
231-
Preconditions.checkArgument(matcher.matches(), "Invalid fullMethodName");
232-
return SpanName.of(matcher.group(1), matcher.group(2));
226+
ApiTracerContext apiTracerContext =
227+
ApiTracerContext.newBuilder()
228+
.setFullMethodName(methodDescriptor.getFullMethodName())
229+
.setTransport(ApiTracerContext.Transport.HTTP)
230+
.setLibraryMetadata(LibraryMetadata.empty())
231+
.build();
232+
return SpanName.of(apiTracerContext);
233233
}
234234
}

sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/rpc/LibraryMetadata.java

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,16 +45,34 @@
4545
@AutoValue
4646
public abstract class LibraryMetadata {
4747

48+
/**
49+
* Returns the repository of the client library.
50+
*
51+
* <p>Example: "googleapis/google-cloud-java". This maps to the {@code gcp.client.repo} attribute.
52+
*
53+
* @return the repository, or {@code null} if not set
54+
*/
4855
@Nullable
4956
public abstract String repository();
5057

58+
/**
59+
* Returns the artifact name of the client library.
60+
*
61+
* <p>Example: "google-cloud-vision". This maps to the {@code gcp.client.artifact} attribute.
62+
*
63+
* @return the artifact name, or {@code null} if not set
64+
*/
5165
@Nullable
5266
public abstract String artifactName();
5367

5468
public static LibraryMetadata empty() {
5569
return newBuilder().build();
5670
}
5771

72+
public boolean isEmpty() {
73+
return repository() == null && artifactName() == null;
74+
}
75+
5876
public static LibraryMetadata.Builder newBuilder() {
5977
return new AutoValue_LibraryMetadata.Builder();
6078
}

0 commit comments

Comments
 (0)