Skip to content

Commit 6c3c53a

Browse files
authored
HBASE-26521 Name RPC spans as $package.$service/$method (#4024)
Signed-off-by: Duo Zhang <zhangduo@apache.org>
1 parent 0c19a5f commit 6c3c53a

File tree

10 files changed

+397
-85
lines changed

10 files changed

+397
-85
lines changed

hbase-client/src/main/java/org/apache/hadoop/hbase/client/trace/ConnectionSpanBuilder.java

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import java.util.Optional;
3232
import java.util.function.Supplier;
3333
import org.apache.hadoop.hbase.client.AsyncConnectionImpl;
34+
import org.apache.hadoop.hbase.security.User;
3435
import org.apache.hadoop.hbase.trace.TraceUtil;
3536
import org.apache.yetus.audience.InterfaceAudience;
3637

@@ -76,15 +77,32 @@ public Span build() {
7677
* Static utility method that performs the primary logic of this builder. It is visible to other
7778
* classes in this package so that other builders can use this functionality as a mix-in.
7879
* @param attributes the attributes map to be populated.
79-
* @param conn the source of attribute values.
80+
* @param conn the source of connection attribute values.
8081
*/
8182
static void populateConnectionAttributes(
8283
final Map<AttributeKey<?>, Object> attributes,
8384
final AsyncConnectionImpl conn
85+
) {
86+
final Supplier<String> connStringSupplier = () -> conn.getConnectionRegistry()
87+
.getConnectionString();
88+
populateConnectionAttributes(attributes, connStringSupplier, conn::getUser);
89+
}
90+
91+
/**
92+
* Static utility method that performs the primary logic of this builder. It is visible to other
93+
* classes in this package so that other builders can use this functionality as a mix-in.
94+
* @param attributes the attributes map to be populated.
95+
* @param connectionStringSupplier the source of the {@code db.connection_string} attribute value.
96+
* @param userSupplier the source of the {@code db.user} attribute value.
97+
*/
98+
static void populateConnectionAttributes(
99+
final Map<AttributeKey<?>, Object> attributes,
100+
final Supplier<String> connectionStringSupplier,
101+
final Supplier<User> userSupplier
84102
) {
85103
attributes.put(DB_SYSTEM, DB_SYSTEM_VALUE);
86-
attributes.put(DB_CONNECTION_STRING, conn.getConnectionRegistry().getConnectionString());
87-
attributes.put(DB_USER, Optional.ofNullable(conn.getUser())
104+
attributes.put(DB_CONNECTION_STRING, connectionStringSupplier.get());
105+
attributes.put(DB_USER, Optional.ofNullable(userSupplier.get())
88106
.map(Object::toString)
89107
.orElse(null));
90108
}
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing, software
13+
* distributed under the License is distributed on an "AS IS" BASIS,
14+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
* See the License for the specific language governing permissions and
16+
* limitations under the License.
17+
*/
18+
19+
package org.apache.hadoop.hbase.client.trace;
20+
21+
import static org.apache.hadoop.hbase.trace.HBaseSemanticAttributes.NET_PEER_NAME;
22+
import static org.apache.hadoop.hbase.trace.HBaseSemanticAttributes.NET_PEER_PORT;
23+
import static org.apache.hadoop.hbase.trace.HBaseSemanticAttributes.RPC_METHOD;
24+
import static org.apache.hadoop.hbase.trace.HBaseSemanticAttributes.RPC_SERVICE;
25+
import static org.apache.hadoop.hbase.trace.HBaseSemanticAttributes.RPC_SYSTEM;
26+
import io.opentelemetry.api.common.AttributeKey;
27+
import io.opentelemetry.api.trace.Span;
28+
import io.opentelemetry.api.trace.SpanBuilder;
29+
import io.opentelemetry.api.trace.SpanKind;
30+
import java.util.HashMap;
31+
import java.util.Map;
32+
import java.util.function.Supplier;
33+
import org.apache.hadoop.hbase.net.Address;
34+
import org.apache.hadoop.hbase.trace.HBaseSemanticAttributes.RpcSystem;
35+
import org.apache.hadoop.hbase.trace.TraceUtil;
36+
import org.apache.yetus.audience.InterfaceAudience;
37+
import org.apache.hbase.thirdparty.com.google.protobuf.Descriptors;
38+
39+
/**
40+
* Construct {@link Span} instances originating from the client side of an IPC.
41+
*
42+
* @see <a href="https://github.com/open-telemetry/opentelemetry-specification/blob/3e380e249f60c3a5f68746f5e84d10195ba41a79/specification/trace/semantic_conventions/rpc.md">Semantic conventions for RPC spans</a>
43+
*/
44+
@InterfaceAudience.Private
45+
public class IpcClientSpanBuilder implements Supplier<Span> {
46+
47+
private String name;
48+
private final Map<AttributeKey<?>, Object> attributes = new HashMap<>();
49+
50+
@Override
51+
public Span get() {
52+
return build();
53+
}
54+
55+
public IpcClientSpanBuilder setMethodDescriptor(final Descriptors.MethodDescriptor md) {
56+
final String packageAndService = getRpcPackageAndService(md.getService());
57+
final String method = getRpcName(md);
58+
this.name = buildSpanName(packageAndService, method);
59+
populateMethodDescriptorAttributes(attributes, md);
60+
return this;
61+
}
62+
63+
public IpcClientSpanBuilder setRemoteAddress(final Address remoteAddress) {
64+
attributes.put(NET_PEER_NAME, remoteAddress.getHostName());
65+
attributes.put(NET_PEER_PORT, (long) remoteAddress.getPort());
66+
return this;
67+
}
68+
69+
@SuppressWarnings("unchecked")
70+
public Span build() {
71+
final SpanBuilder builder = TraceUtil.getGlobalTracer()
72+
.spanBuilder(name)
73+
// TODO: what about clients embedded in Master/RegionServer/Gateways/&c?
74+
.setSpanKind(SpanKind.CLIENT);
75+
attributes.forEach((k, v) -> builder.setAttribute((AttributeKey<? super Object>) k, v));
76+
return builder.startSpan();
77+
}
78+
79+
/**
80+
* Static utility method that performs the primary logic of this builder. It is visible to other
81+
* classes in this package so that other builders can use this functionality as a mix-in.
82+
* @param attributes the attributes map to be populated.
83+
* @param md the source of the RPC attribute values.
84+
*/
85+
static void populateMethodDescriptorAttributes(
86+
final Map<AttributeKey<?>, Object> attributes,
87+
final Descriptors.MethodDescriptor md
88+
) {
89+
final String packageAndService = getRpcPackageAndService(md.getService());
90+
final String method = getRpcName(md);
91+
attributes.put(RPC_SYSTEM, RpcSystem.HBASE_RPC.name());
92+
attributes.put(RPC_SERVICE, packageAndService);
93+
attributes.put(RPC_METHOD, method);
94+
}
95+
96+
/**
97+
* Retrieve the combined {@code $package.$service} value from {@code sd}.
98+
*/
99+
public static String getRpcPackageAndService(final Descriptors.ServiceDescriptor sd) {
100+
// it happens that `getFullName` returns a string in the $package.$service format required by
101+
// the otel RPC specification. Use it for now; might have to parse the value in the future.
102+
return sd.getFullName();
103+
}
104+
105+
/**
106+
* Retrieve the {@code $method} value from {@code md}.
107+
*/
108+
public static String getRpcName(final Descriptors.MethodDescriptor md) {
109+
return md.getName();
110+
}
111+
112+
/**
113+
* Construct an RPC span name.
114+
*/
115+
public static String buildSpanName(final String packageAndService, final String method) {
116+
return packageAndService + "/" + method;
117+
}
118+
}

hbase-client/src/main/java/org/apache/hadoop/hbase/ipc/AbstractRpcClient.java

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,6 @@
2020

2121
import static org.apache.hadoop.hbase.ipc.IPCUtil.toIOE;
2222
import static org.apache.hadoop.hbase.ipc.IPCUtil.wrapException;
23-
import static org.apache.hadoop.hbase.trace.HBaseSemanticAttributes.REMOTE_HOST_KEY;
24-
import static org.apache.hadoop.hbase.trace.HBaseSemanticAttributes.REMOTE_PORT_KEY;
25-
import static org.apache.hadoop.hbase.trace.HBaseSemanticAttributes.RPC_METHOD_KEY;
26-
import static org.apache.hadoop.hbase.trace.HBaseSemanticAttributes.RPC_SERVICE_KEY;
2723

2824
import io.opentelemetry.api.trace.Span;
2925
import io.opentelemetry.api.trace.StatusCode;
@@ -40,6 +36,7 @@
4036
import org.apache.hadoop.hbase.HConstants;
4137
import org.apache.hadoop.hbase.ServerName;
4238
import org.apache.hadoop.hbase.client.MetricsConnection;
39+
import org.apache.hadoop.hbase.client.trace.IpcClientSpanBuilder;
4340
import org.apache.hadoop.hbase.codec.Codec;
4441
import org.apache.hadoop.hbase.codec.KeyValueCodec;
4542
import org.apache.hadoop.hbase.net.Address;
@@ -399,11 +396,10 @@ private void onCallFinished(Call call, HBaseRpcController hrc, Address addr,
399396
private Call callMethod(final Descriptors.MethodDescriptor md, final HBaseRpcController hrc,
400397
final Message param, Message returnType, final User ticket, final Address addr,
401398
final RpcCallback<Message> callback) {
402-
Span span = TraceUtil.createClientSpan("RpcClient.callMethod")
403-
.setAttribute(RPC_SERVICE_KEY, md.getService().getName())
404-
.setAttribute(RPC_METHOD_KEY, md.getName())
405-
.setAttribute(REMOTE_HOST_KEY, addr.getHostName())
406-
.setAttribute(REMOTE_PORT_KEY, addr.getPort());
399+
Span span = new IpcClientSpanBuilder()
400+
.setMethodDescriptor(md)
401+
.setRemoteAddress(addr)
402+
.build();
407403
try (Scope scope = span.makeCurrent()) {
408404
final MetricsConnection.CallStats cs = MetricsConnection.newCallStats();
409405
cs.setStartTime(EnvironmentEdgeManager.currentTime());

hbase-client/src/test/java/org/apache/hadoop/hbase/client/trace/hamcrest/SpanDataMatchers.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import io.opentelemetry.api.trace.StatusCode;
2525
import io.opentelemetry.sdk.trace.data.SpanData;
2626
import io.opentelemetry.sdk.trace.data.StatusData;
27+
import java.time.Duration;
2728
import org.hamcrest.Description;
2829
import org.hamcrest.FeatureMatcher;
2930
import org.hamcrest.Matcher;
@@ -46,6 +47,16 @@ public static Matcher<SpanData> hasAttributes(Matcher<Attributes> matcher) {
4647
};
4748
}
4849

50+
public static Matcher<SpanData> hasDuration(Matcher<Duration> matcher) {
51+
return new FeatureMatcher<SpanData, Duration>(
52+
matcher, "SpanData having duration that ", "duration") {
53+
@Override
54+
protected Duration featureValueOf(SpanData item) {
55+
return Duration.ofNanos(item.getEndEpochNanos() - item.getStartEpochNanos());
56+
}
57+
};
58+
}
59+
4960
public static Matcher<SpanData> hasEnded() {
5061
return new TypeSafeMatcher<SpanData>() {
5162
@Override protected boolean matchesSafely(SpanData item) {
@@ -92,4 +103,17 @@ public static Matcher<SpanData> hasStatusWithCode(StatusCode statusCode) {
92103
}
93104
};
94105
}
106+
107+
public static Matcher<SpanData> hasTraceId(String traceId) {
108+
return hasTraceId(is(equalTo(traceId)));
109+
}
110+
111+
public static Matcher<SpanData> hasTraceId(Matcher<String> matcher) {
112+
return new FeatureMatcher<SpanData, String>(
113+
matcher, "SpanData with a traceId that ", "traceId") {
114+
@Override protected String featureValueOf(SpanData item) {
115+
return item.getTraceId();
116+
}
117+
};
118+
}
95119
}

hbase-common/src/main/java/org/apache/hadoop/hbase/trace/HBaseSemanticAttributes.java

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -44,14 +44,13 @@ public final class HBaseSemanticAttributes {
4444
AttributeKey.stringArrayKey("db.hbase.container_operations");
4545
public static final AttributeKey<List<String>> REGION_NAMES_KEY =
4646
AttributeKey.stringArrayKey("db.hbase.regions");
47-
public static final AttributeKey<String> RPC_SERVICE_KEY =
48-
AttributeKey.stringKey("db.hbase.rpc.service");
49-
public static final AttributeKey<String> RPC_METHOD_KEY =
50-
AttributeKey.stringKey("db.hbase.rpc.method");
47+
public static final AttributeKey<String> RPC_SYSTEM = SemanticAttributes.RPC_SYSTEM;
48+
public static final AttributeKey<String> RPC_SERVICE = SemanticAttributes.RPC_SERVICE;
49+
public static final AttributeKey<String> RPC_METHOD = SemanticAttributes.RPC_METHOD;
5150
public static final AttributeKey<String> SERVER_NAME_KEY =
5251
AttributeKey.stringKey("db.hbase.server.name");
53-
public static final AttributeKey<String> REMOTE_HOST_KEY = SemanticAttributes.NET_PEER_NAME;
54-
public static final AttributeKey<Long> REMOTE_PORT_KEY = SemanticAttributes.NET_PEER_PORT;
52+
public static final AttributeKey<String> NET_PEER_NAME = SemanticAttributes.NET_PEER_NAME;
53+
public static final AttributeKey<Long> NET_PEER_PORT = SemanticAttributes.NET_PEER_PORT;
5554
public static final AttributeKey<Boolean> ROW_LOCK_READ_LOCK_KEY =
5655
AttributeKey.booleanKey("db.hbase.rowlock.readlock");
5756
public static final AttributeKey<String> WAL_IMPL = AttributeKey.stringKey("db.hbase.wal.impl");
@@ -74,5 +73,13 @@ public enum Operation {
7473
SCAN,
7574
}
7675

76+
/**
77+
* These are values used with {@link #RPC_SYSTEM}. Only a single value for now; more to come as
78+
* we add tracing over our gateway components.
79+
*/
80+
public enum RpcSystem {
81+
HBASE_RPC,
82+
}
83+
7784
private HBaseSemanticAttributes() { }
7885
}

hbase-server/pom.xml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,12 @@
255255
<groupId>org.apache.hbase</groupId>
256256
<artifactId>hbase-common</artifactId>
257257
</dependency>
258+
<dependency>
259+
<groupId>org.apache.hbase</groupId>
260+
<artifactId>hbase-client</artifactId>
261+
<type>test-jar</type>
262+
<scope>test</scope>
263+
</dependency>
258264
<dependency>
259265
<groupId>org.apache.hbase</groupId>
260266
<artifactId>hbase-http</artifactId>

hbase-server/src/main/java/org/apache/hadoop/hbase/ipc/CallRunner.java

Lines changed: 2 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,8 @@
1717
*/
1818
package org.apache.hadoop.hbase.ipc;
1919

20-
import static org.apache.hadoop.hbase.trace.HBaseSemanticAttributes.RPC_METHOD_KEY;
21-
import static org.apache.hadoop.hbase.trace.HBaseSemanticAttributes.RPC_SERVICE_KEY;
2220
import io.opentelemetry.api.trace.Span;
2321
import io.opentelemetry.api.trace.StatusCode;
24-
import io.opentelemetry.context.Context;
2522
import io.opentelemetry.context.Scope;
2623
import java.net.InetSocketAddress;
2724
import java.nio.channels.ClosedChannelException;
@@ -32,6 +29,7 @@
3229
import org.apache.hadoop.hbase.exceptions.TimeoutIOException;
3330
import org.apache.hadoop.hbase.monitoring.MonitoredRPCHandler;
3431
import org.apache.hadoop.hbase.security.User;
32+
import org.apache.hadoop.hbase.server.trace.IpcServerSpanBuilder;
3533
import org.apache.hadoop.hbase.trace.TraceUtil;
3634
import org.apache.hadoop.hbase.util.EnvironmentEdgeManager;
3735
import org.apache.hadoop.hbase.util.Pair;
@@ -90,14 +88,6 @@ private void cleanup() {
9088
this.rpcServer = null;
9189
}
9290

93-
private String getServiceName() {
94-
return call.getService() != null ? call.getService().getDescriptorForType().getName() : "";
95-
}
96-
97-
private String getMethodName() {
98-
return call.getMethod() != null ? call.getMethod().getName() : "";
99-
}
100-
10191
public void run() {
10292
try {
10393
if (call.disconnectSince() >= 0) {
@@ -122,12 +112,7 @@ public void run() {
122112
String error = null;
123113
Pair<Message, CellScanner> resultPair = null;
124114
RpcServer.CurCall.set(call);
125-
String serviceName = getServiceName();
126-
String methodName = getMethodName();
127-
Span span = TraceUtil.getGlobalTracer().spanBuilder("RpcServer.callMethod")
128-
.setParent(Context.current().with(((ServerCall<?>) call).getSpan())).startSpan()
129-
.setAttribute(RPC_SERVICE_KEY, serviceName)
130-
.setAttribute(RPC_METHOD_KEY, methodName);
115+
Span span = new IpcServerSpanBuilder(call).build();
131116
try (Scope traceScope = span.makeCurrent()) {
132117
if (!this.rpcServer.isStarted()) {
133118
InetSocketAddress address = rpcServer.getListenerAddress();

0 commit comments

Comments
 (0)