Skip to content

Commit 557c4fc

Browse files
YaoYingLongtrasklaurit
authored
feat: Add support for HBase versions 2.0.0 to 2.5.0. (#18253)
Co-authored-by: Trask Stalnaker <trask.stalnaker@gmail.com> Co-authored-by: Lauri Tulmin <ltulmin@splunk.com>
1 parent 7859038 commit 557c4fc

20 files changed

Lines changed: 1220 additions & 0 deletions

File tree

.fossa.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,9 @@ targets:
139139
- type: gradle
140140
path: ./
141141
target: ':instrumentation:gwt-2.0:javaagent'
142+
- type: gradle
143+
path: ./
144+
target: ':instrumentation:hbase-client-2.0:javaagent'
142145
- type: gradle
143146
path: ./
144147
target: ':instrumentation:helidon-4.3:javaagent'

.github/config/latest-dep-versions.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,10 @@
297297
"org.apache.dubbo:dubbo#+": "3.3.6",
298298
"org.apache.dubbo:dubbo-config-api#+": "3.3.6",
299299
"org.apache.geode:geode-core#+": "2.0.2",
300+
"org.apache.hbase:hbase-client#+": "2.6.5",
301+
"org.apache.hbase:hbase-client#2.4.+": "2.4.18",
302+
"org.apache.hbase:hbase-shaded-client#+": "2.4.18",
303+
"org.apache.hbase:hbase-shaded-client#2.4.+": "2.4.18",
300304
"org.apache.httpcomponents.client5:httpclient5#+": "5.6.1",
301305
"org.apache.httpcomponents:httpasyncclient#+": "4.1.5",
302306
"org.apache.httpcomponents:httpclient#+": "4.5.14",

docs/supported-libraries.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ These are the supported libraries and frameworks:
3030
| [Apache DBCP](https://commons.apache.org/proper/commons-dbcp/) | 2.0+ | [opentelemetry-apache-dbcp-2.0](../instrumentation/apache-dbcp-2.0/library) | [Database Pool Metrics] |
3131
| [Apache Dubbo](https://github.com/apache/dubbo/) | 2.7+ | [opentelemetry-apache-dubbo-2.7](../instrumentation/apache-dubbo-2.7/library-autoconfigure) | [RPC Client Spans], [RPC Server Spans] |
3232
| [Apache ElasticJob](https://shardingsphere.apache.org/elasticjob/) | 3.0+ | N/A | none |
33+
| [Apache HBase](https://hbase.apache.org/) | 2.0 - 2.4.x | N/A | [Database Client Spans], [Database Client Metrics]&nbsp;[6] |
3334
| [Apache HttpAsyncClient](https://hc.apache.org/index.html) | 4.1+ | N/A | [HTTP Client Spans], [HTTP Client Metrics] |
3435
| [Apache HttpClient](https://hc.apache.org/index.html) | 2.0+ | [opentelemetry-apache-httpclient-4.3](../instrumentation/apache-httpclient/apache-httpclient-4.3/library),<br>[opentelemetry-apache-httpclient-5.2](../instrumentation/apache-httpclient/apache-httpclient-5.2/library) | [HTTP Client Spans], [HTTP Client Metrics] |
3536
| [Apache Iceberg](https://iceberg.apache.org/) | N/A | [opentelemetry-iceberg-1.8](../instrumentation/iceberg-1.8/library/) | none |
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
plugins {
2+
id("otel.javaagent-instrumentation")
3+
}
4+
5+
otelJava {
6+
// HBase 2.0.x test stack is not reliable on JDK 25+.
7+
maxJavaVersionForTests.set(JavaVersion.VERSION_24)
8+
}
9+
10+
muzzle {
11+
pass {
12+
group.set("org.apache.hbase")
13+
module.set("hbase-client")
14+
versions.set("[2.0.0, 2.5.0)")
15+
assertInverse.set(true)
16+
}
17+
}
18+
19+
dependencies {
20+
library("org.apache.hbase:hbase-client:2.0.0")
21+
22+
compileOnly("com.google.auto.value:auto-value-annotations")
23+
annotationProcessor("com.google.auto.value:auto-value")
24+
25+
testImplementation(project(":instrumentation:hbase-client-2.0:testing"))
26+
27+
latestDepTestLibrary("org.apache.hbase:hbase-client:2.4.+") // native on-by-default instrumentation after this version
28+
}
29+
30+
testing {
31+
suites {
32+
val shadedClientTest by registering(JvmTestSuite::class) {
33+
dependencies {
34+
implementation("org.apache.hbase:hbase-shaded-client:${baseVersion("2.0.0").orLatest("2.4.+")}")
35+
implementation(project(":instrumentation:hbase-client-2.0:testing"))
36+
}
37+
}
38+
}
39+
}
40+
41+
abstract class HbaseBuildService : BuildService<BuildServiceParameters.None>
42+
43+
// HBase test container binds fixed host ports, disallow running tests in parallel.
44+
gradle.sharedServices.registerIfAbsent("hbaseBuildService", HbaseBuildService::class.java) {
45+
maxParallelUsages.convention(1)
46+
}
47+
48+
tasks {
49+
withType<Test>().configureEach {
50+
usesService(gradle.sharedServices.registrations["testcontainersBuildService"].service)
51+
usesService(gradle.sharedServices.registrations["hbaseBuildService"].service)
52+
systemProperty("collectMetadata", otelProps.collectMetadata)
53+
}
54+
55+
val stableSemconvSuites = testing.suites.withType(JvmTestSuite::class)
56+
.map { suite ->
57+
register<Test>("${suite.name}StableSemconv") {
58+
testClassesDirs = suite.sources.output.classesDirs
59+
classpath = suite.sources.runtimeClasspath
60+
61+
jvmArgs("-Dotel.semconv-stability.opt-in=database")
62+
systemProperty("metadataConfig", "otel.semconv-stability.opt-in=database")
63+
}
64+
}
65+
66+
check {
67+
dependsOn(testing.suites, stableSemconvSuites)
68+
}
69+
70+
if (otelProps.denyUnsafe) {
71+
withType<Test>().configureEach {
72+
enabled = false
73+
}
74+
}
75+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.hbase.client.v2_0;
7+
8+
import static io.opentelemetry.javaagent.instrumentation.hbase.client.v2_0.HbaseSingletons.getTableName;
9+
import static io.opentelemetry.javaagent.instrumentation.hbase.client.v2_0.HbaseSingletons.instrumenter;
10+
import static io.opentelemetry.javaagent.instrumentation.hbase.client.v2_0.HbaseSingletons.resetRequestAndContext;
11+
import static io.opentelemetry.javaagent.instrumentation.hbase.client.v2_0.HbaseSingletons.setRequestAndContext;
12+
import static net.bytebuddy.matcher.ElementMatchers.named;
13+
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
14+
15+
import io.opentelemetry.context.Context;
16+
import io.opentelemetry.context.Scope;
17+
import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge;
18+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
19+
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
20+
import java.net.InetSocketAddress;
21+
import javax.annotation.Nullable;
22+
import net.bytebuddy.asm.Advice;
23+
import net.bytebuddy.description.type.TypeDescription;
24+
import net.bytebuddy.matcher.ElementMatcher;
25+
import org.apache.hadoop.hbase.net.Address;
26+
import org.apache.hadoop.hbase.security.User;
27+
import org.apache.hbase.thirdparty.com.google.protobuf.Descriptors;
28+
29+
class AbstractRpcClientInstrumentation implements TypeInstrumentation {
30+
31+
@Override
32+
public ElementMatcher<TypeDescription> typeMatcher() {
33+
return named("org.apache.hadoop.hbase.ipc.AbstractRpcClient");
34+
}
35+
36+
@Override
37+
public void transform(TypeTransformer transformer) {
38+
transformer.applyAdviceToMethod(
39+
named("callMethod")
40+
.and(
41+
takesArgument(
42+
0,
43+
named(
44+
"org.apache.hbase.thirdparty.com.google.protobuf.Descriptors$MethodDescriptor")))
45+
.and(takesArgument(4, named("org.apache.hadoop.hbase.security.User"))),
46+
getClass().getName() + "$CallMethodAdvice");
47+
}
48+
49+
@SuppressWarnings("unused")
50+
public static class CallMethodAdvice {
51+
@Advice.OnMethodEnter(suppress = Throwable.class)
52+
public static RequestAndContext onEnter(
53+
@Advice.Argument(0) Descriptors.MethodDescriptor md,
54+
@Advice.Argument(4) User ticket,
55+
@Advice.Argument(5) Object addr) {
56+
String hostname = null;
57+
Integer port = null;
58+
if (addr instanceof Address) {
59+
Address address = (Address) addr;
60+
port = address.getPort();
61+
hostname = address.getHostname();
62+
} else if (addr instanceof InetSocketAddress) {
63+
InetSocketAddress address = (InetSocketAddress) addr;
64+
port = address.getPort();
65+
hostname = address.getHostString();
66+
}
67+
HbaseRequest request =
68+
HbaseRequest.create(md.getName(), getTableName(), ticket.getName(), hostname, port);
69+
Context parentContext = Java8BytecodeBridge.currentContext();
70+
if (!instrumenter().shouldStart(parentContext, request)) {
71+
return null;
72+
}
73+
Context context = instrumenter().start(parentContext, request);
74+
Scope scope = context.makeCurrent();
75+
RequestAndContext requestAndContext = RequestAndContext.create(request, scope, context);
76+
setRequestAndContext(requestAndContext);
77+
return requestAndContext;
78+
}
79+
80+
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
81+
public static void onExit(
82+
@Advice.Thrown Throwable throwable,
83+
@Advice.Enter @Nullable RequestAndContext requestAndContext) {
84+
resetRequestAndContext();
85+
if (requestAndContext == null) {
86+
return;
87+
}
88+
89+
Scope scope = requestAndContext.getScope();
90+
scope.close();
91+
92+
if (throwable != null) {
93+
instrumenter()
94+
.end(requestAndContext.getContext(), requestAndContext.getRequest(), null, throwable);
95+
}
96+
}
97+
}
98+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.hbase.client.v2_0;
7+
8+
import io.opentelemetry.instrumentation.api.incubator.semconv.db.DbClientAttributesGetter;
9+
import io.opentelemetry.semconv.incubating.DbIncubatingAttributes.DbSystemNameIncubatingValues;
10+
import java.net.InetSocketAddress;
11+
import javax.annotation.Nullable;
12+
13+
final class HbaseAttributesGetter implements DbClientAttributesGetter<HbaseRequest, Void> {
14+
15+
@Nullable
16+
@Override
17+
public String getDbSystemName(HbaseRequest hbaseRequest) {
18+
return DbSystemNameIncubatingValues.HBASE;
19+
}
20+
21+
@Nullable
22+
@Override
23+
public String getDbNamespace(HbaseRequest hbaseRequest) {
24+
return hbaseRequest.getTable();
25+
}
26+
27+
@Nullable
28+
@Override
29+
// Old database semconv still use db.user, so we must implement the deprecated hook.
30+
@SuppressWarnings("deprecation")
31+
public String getUser(HbaseRequest hbaseRequest) {
32+
return hbaseRequest.getUser();
33+
}
34+
35+
@Nullable
36+
@Override
37+
public String getDbQueryText(HbaseRequest hbaseRequest) {
38+
return null;
39+
}
40+
41+
@Nullable
42+
@Override
43+
public String getDbOperationName(HbaseRequest hbaseRequest) {
44+
return hbaseRequest.getOperation();
45+
}
46+
47+
@Nullable
48+
@Override
49+
public InetSocketAddress getNetworkPeerInetSocketAddress(
50+
HbaseRequest request, @Nullable Void unused) {
51+
if (request.getHost() == null || request.getPort() == null) {
52+
return null;
53+
}
54+
return InetSocketAddress.createUnresolved(request.getHost(), request.getPort());
55+
}
56+
57+
@Nullable
58+
@Override
59+
public String getServerAddress(HbaseRequest request) {
60+
return request.getHost();
61+
}
62+
63+
@Nullable
64+
@Override
65+
public Integer getServerPort(HbaseRequest request) {
66+
return request.getPort();
67+
}
68+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.hbase.client.v2_0;
7+
8+
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
9+
import static java.util.Arrays.asList;
10+
import static java.util.Collections.singletonList;
11+
import static net.bytebuddy.matcher.ElementMatchers.not;
12+
13+
import com.google.auto.service.AutoService;
14+
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
15+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
16+
import io.opentelemetry.javaagent.extension.instrumentation.internal.ExperimentalInstrumentationModule;
17+
import java.util.List;
18+
import net.bytebuddy.matcher.ElementMatcher;
19+
20+
@AutoService(InstrumentationModule.class)
21+
public final class HbaseInstrumentationModule extends InstrumentationModule
22+
implements ExperimentalInstrumentationModule {
23+
24+
private static final String CALL_UTIL = "org.apache.hadoop.hbase.ipc.OpenTelemetryCallUtil";
25+
26+
public HbaseInstrumentationModule() {
27+
super("hbase-client", "hbase-client-2.0");
28+
}
29+
30+
@Override
31+
public ElementMatcher.Junction<ClassLoader> classLoaderMatcher() {
32+
return hasClassesNamed(
33+
// added in 2.0.0
34+
"org.apache.hadoop.hbase.ipc.RpcConnection",
35+
"org.apache.hadoop.hbase.client.AsyncAdmin")
36+
// added in 2.5.0 (native OTel support)
37+
.and(not(hasClassesNamed("org.apache.hadoop.hbase.client.trace.IpcClientSpanBuilder")));
38+
}
39+
40+
@Override
41+
public boolean isHelperClass(String className) {
42+
return CALL_UTIL.equals(className);
43+
}
44+
45+
@Override
46+
public List<String> injectedClassNames() {
47+
return singletonList(CALL_UTIL);
48+
}
49+
50+
@Override
51+
public List<TypeInstrumentation> typeInstrumentations() {
52+
return asList(
53+
new RegionServerCallableInstrumentation(),
54+
new AbstractRpcClientInstrumentation(),
55+
new RpcConnectionInstrumentation(),
56+
new IpcCallInstrumentation());
57+
}
58+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
* SPDX-License-Identifier: Apache-2.0
4+
*/
5+
6+
package io.opentelemetry.javaagent.instrumentation.hbase.client.v2_0;
7+
8+
import com.google.auto.value.AutoValue;
9+
import javax.annotation.Nullable;
10+
11+
@AutoValue
12+
public abstract class HbaseRequest {
13+
14+
public static HbaseRequest create(
15+
@Nullable String operation,
16+
@Nullable String table,
17+
@Nullable String user,
18+
@Nullable String host,
19+
@Nullable Integer port) {
20+
return new AutoValue_HbaseRequest(operation, table, user, host, port);
21+
}
22+
23+
@Nullable
24+
public abstract String getOperation();
25+
26+
@Nullable
27+
public abstract String getTable();
28+
29+
@Nullable
30+
public abstract String getUser();
31+
32+
@Nullable
33+
public abstract String getHost();
34+
35+
@Nullable
36+
public abstract Integer getPort();
37+
}

0 commit comments

Comments
 (0)