Skip to content

Commit fe79c38

Browse files
committed
Add Elasticsearch Java client plugin for 7.x-9.x
Add plugin for co.elastic.clients:elasticsearch-java, the modern Elasticsearch Java client that replaces the deprecated REST High Level Client. Covers both 7.x (RestClientTransport.performRequest) and 8.x/9.x (ElasticsearchTransportBase.performRequest) code paths using witness classes for version detection. Tested versions: 7.17.22, 8.5.3, 8.12.2, 8.17.0, 9.0.0. Local verification passed for 8.17.0.
1 parent e8795e9 commit fe79c38

File tree

21 files changed

+914
-0
lines changed

21 files changed

+914
-0
lines changed

.github/workflows/plugins-test.2.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ jobs:
9797
- nacos-client-2.x-scenario
9898
- rocketmq-scenario
9999
- rocketmq-5-grpc-scenario
100+
- elasticsearch-java-scenario
100101
steps:
101102
- uses: actions/checkout@v2
102103
with:

CHANGES.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Release Notes.
2121
* Enhance test/plugin/run.sh to support extra Maven properties per version in support-version.list (format: version,key=value).
2222
* Add MariaDB 3.x plugin (all classes renamed in 3.x).
2323
* Extend Jedis 4.x plugin to support Jedis 5.x (fix witness method for 5.x compatibility).
24+
* Add Elasticsearch Java client (co.elastic.clients:elasticsearch-java) plugin for 7.x-9.x.
2425

2526
All issues and pull requests are [here](https://github.com/apache/skywalking/milestone/249?closed=1)
2627

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
~ Licensed to the Apache Software Foundation (ASF) under one or more
4+
~ contributor license agreements. See the NOTICE file distributed with
5+
~ this work for additional information regarding copyright ownership.
6+
~ The ASF licenses this file to You under the Apache License, Version 2.0
7+
~ (the "License"); you may not use this file except in compliance with
8+
~ 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+
20+
<project xmlns="http://maven.apache.org/POM/4.0.0"
21+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
22+
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
23+
<modelVersion>4.0.0</modelVersion>
24+
25+
<parent>
26+
<artifactId>apm-sdk-plugin</artifactId>
27+
<groupId>org.apache.skywalking</groupId>
28+
<version>9.7.0-SNAPSHOT</version>
29+
</parent>
30+
31+
<artifactId>apm-elasticsearch-java-plugin</artifactId>
32+
<packaging>jar</packaging>
33+
34+
<name>elasticsearch-java-plugin</name>
35+
36+
<properties>
37+
<elasticsearch-java.version>8.17.0</elasticsearch-java.version>
38+
</properties>
39+
40+
<dependencies>
41+
<dependency>
42+
<groupId>co.elastic.clients</groupId>
43+
<artifactId>elasticsearch-java</artifactId>
44+
<version>${elasticsearch-java.version}</version>
45+
<scope>provided</scope>
46+
</dependency>
47+
</dependencies>
48+
</project>
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
package org.apache.skywalking.apm.plugin.elasticsearch.java.define;
20+
21+
import net.bytebuddy.description.method.MethodDescription;
22+
import net.bytebuddy.matcher.ElementMatcher;
23+
import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint;
24+
import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
25+
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
26+
import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch;
27+
28+
import static net.bytebuddy.matcher.ElementMatchers.named;
29+
import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName;
30+
31+
/**
32+
* Enhance {@code co.elastic.clients.transport.ElasticsearchTransportBase}
33+
* which exists in elasticsearch-java 8.x+. The performRequest method
34+
* moved from RestClientTransport to this base class in 8.x.
35+
* <p>
36+
* The peer is propagated from RestClientTransport (subclass) via dynamic field.
37+
*/
38+
public class ElasticsearchTransportBaseInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
39+
40+
private static final String ENHANCE_CLASS = "co.elastic.clients.transport.ElasticsearchTransportBase";
41+
42+
private static final String PERFORM_REQUEST_INTERCEPTOR =
43+
"org.apache.skywalking.apm.plugin.elasticsearch.java.interceptor.TransportPerformRequestInterceptor";
44+
45+
@Override
46+
protected String[] witnessClasses() {
47+
return new String[] {ENHANCE_CLASS};
48+
}
49+
50+
@Override
51+
protected ClassMatch enhanceClass() {
52+
return byName(ENHANCE_CLASS);
53+
}
54+
55+
@Override
56+
public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
57+
return new ConstructorInterceptPoint[0];
58+
}
59+
60+
@Override
61+
public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
62+
return new InstanceMethodsInterceptPoint[] {
63+
new InstanceMethodsInterceptPoint() {
64+
@Override
65+
public ElementMatcher<MethodDescription> getMethodsMatcher() {
66+
return named("performRequest");
67+
}
68+
69+
@Override
70+
public String getMethodsInterceptor() {
71+
return PERFORM_REQUEST_INTERCEPTOR;
72+
}
73+
74+
@Override
75+
public boolean isOverrideArgs() {
76+
return false;
77+
}
78+
}
79+
};
80+
}
81+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
package org.apache.skywalking.apm.plugin.elasticsearch.java.define;
20+
21+
import net.bytebuddy.description.method.MethodDescription;
22+
import net.bytebuddy.matcher.ElementMatcher;
23+
import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint;
24+
import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
25+
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
26+
import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch;
27+
28+
import static net.bytebuddy.matcher.ElementMatchers.named;
29+
import static org.apache.skywalking.apm.agent.core.plugin.bytebuddy.ArgumentTypeNameMatch.takesArgumentWithType;
30+
import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName;
31+
32+
/**
33+
* Enhance {@code co.elastic.clients.transport.rest_client.RestClientTransport}
34+
* for the Elasticsearch Java client (co.elastic.clients:elasticsearch-java).
35+
* <p>
36+
* Covers both 7.x (performRequest on RestClientTransport) and 8.x+
37+
* (performRequest inherited from ElasticsearchTransportBase, but constructor on RestClientTransport).
38+
*/
39+
public class RestClientTransportInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
40+
41+
private static final String ENHANCE_CLASS = "co.elastic.clients.transport.rest_client.RestClientTransport";
42+
43+
private static final String CONSTRUCTOR_INTERCEPTOR =
44+
"org.apache.skywalking.apm.plugin.elasticsearch.java.interceptor.RestClientTransportConstructorInterceptor";
45+
46+
private static final String PERFORM_REQUEST_INTERCEPTOR =
47+
"org.apache.skywalking.apm.plugin.elasticsearch.java.interceptor.TransportPerformRequestInterceptor";
48+
49+
@Override
50+
protected String[] witnessClasses() {
51+
return new String[] {ENHANCE_CLASS};
52+
}
53+
54+
@Override
55+
protected ClassMatch enhanceClass() {
56+
return byName(ENHANCE_CLASS);
57+
}
58+
59+
@Override
60+
public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
61+
return new ConstructorInterceptPoint[] {
62+
new ConstructorInterceptPoint() {
63+
@Override
64+
public ElementMatcher<MethodDescription> getConstructorMatcher() {
65+
return takesArgumentWithType(0, "org.elasticsearch.client.RestClient");
66+
}
67+
68+
@Override
69+
public String getConstructorInterceptor() {
70+
return CONSTRUCTOR_INTERCEPTOR;
71+
}
72+
}
73+
};
74+
}
75+
76+
@Override
77+
public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
78+
return new InstanceMethodsInterceptPoint[] {
79+
new InstanceMethodsInterceptPoint() {
80+
@Override
81+
public ElementMatcher<MethodDescription> getMethodsMatcher() {
82+
return named("performRequest");
83+
}
84+
85+
@Override
86+
public String getMethodsInterceptor() {
87+
return PERFORM_REQUEST_INTERCEPTOR;
88+
}
89+
90+
@Override
91+
public boolean isOverrideArgs() {
92+
return false;
93+
}
94+
}
95+
};
96+
}
97+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
package org.apache.skywalking.apm.plugin.elasticsearch.java.interceptor;
20+
21+
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
22+
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor;
23+
import org.elasticsearch.client.Node;
24+
import org.elasticsearch.client.RestClient;
25+
26+
import java.util.List;
27+
import java.util.stream.Collectors;
28+
29+
/**
30+
* Intercept RestClientTransport constructor to extract peer address from RestClient nodes.
31+
*/
32+
public class RestClientTransportConstructorInterceptor implements InstanceConstructorInterceptor {
33+
34+
@Override
35+
public void onConstruct(EnhancedInstance objInst, Object[] allArguments) {
36+
RestClient restClient = (RestClient) allArguments[0];
37+
List<Node> nodes = restClient.getNodes();
38+
String peers = nodes.stream()
39+
.map(node -> node.getHost().toHostString())
40+
.collect(Collectors.joining(","));
41+
objInst.setSkyWalkingDynamicField(peers);
42+
}
43+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*/
18+
19+
package org.apache.skywalking.apm.plugin.elasticsearch.java.interceptor;
20+
21+
import co.elastic.clients.transport.Endpoint;
22+
import org.apache.skywalking.apm.agent.core.context.ContextManager;
23+
import org.apache.skywalking.apm.agent.core.context.tag.Tags;
24+
import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
25+
import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer;
26+
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
27+
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
28+
import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
29+
import org.apache.skywalking.apm.network.trace.component.ComponentsDefine;
30+
31+
import java.lang.reflect.Method;
32+
33+
/**
34+
* Intercept ElasticsearchTransport.performRequest() to create exit spans.
35+
* <p>
36+
* Args: [0] request, [1] endpoint (Endpoint), [2] options (TransportOptions)
37+
* The endpoint.id() provides the operation name (e.g., "search", "index", "bulk").
38+
*/
39+
public class TransportPerformRequestInterceptor implements InstanceMethodsAroundInterceptor {
40+
41+
private static final String DB_TYPE = "Elasticsearch";
42+
43+
@Override
44+
public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments,
45+
Class<?>[] argumentsTypes, MethodInterceptResult result) throws Throwable {
46+
Endpoint<?, ?, ?> endpoint = (Endpoint<?, ?, ?>) allArguments[1];
47+
String operationName = "Elasticsearch/" + endpoint.id();
48+
49+
String peers = (String) objInst.getSkyWalkingDynamicField();
50+
if (peers == null || peers.isEmpty()) {
51+
peers = "Unknown";
52+
}
53+
54+
AbstractSpan span = ContextManager.createExitSpan(operationName, peers);
55+
span.setComponent(ComponentsDefine.REST_HIGH_LEVEL_CLIENT);
56+
Tags.DB_TYPE.set(span, DB_TYPE);
57+
SpanLayer.asDB(span);
58+
}
59+
60+
@Override
61+
public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments,
62+
Class<?>[] argumentsTypes, Object ret) throws Throwable {
63+
ContextManager.stopSpan();
64+
return ret;
65+
}
66+
67+
@Override
68+
public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments,
69+
Class<?>[] argumentsTypes, Throwable t) {
70+
ContextManager.activeSpan().log(t);
71+
ContextManager.activeSpan().errorOccurred();
72+
}
73+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
# Elasticsearch Java client (co.elastic.clients:elasticsearch-java) 7.x-9.x
18+
elasticsearch-java=org.apache.skywalking.apm.plugin.elasticsearch.java.define.RestClientTransportInstrumentation
19+
elasticsearch-java=org.apache.skywalking.apm.plugin.elasticsearch.java.define.ElasticsearchTransportBaseInstrumentation

apm-sniffer/apm-sdk-plugin/pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@
7171
<module>elasticsearch-5.x-plugin</module>
7272
<module>elasticsearch-6.x-plugin</module>
7373
<module>elasticsearch-7.x-plugin</module>
74+
<module>elasticsearch-java-plugin</module>
7475
<module>undertow-plugins</module>
7576
<module>rabbitmq-plugin</module>
7677
<module>dubbo-conflict-patch</module>

docs/en/setup/service-agent/java-agent/Plugin-list.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
- elasticsearch-5.x
2626
- elasticsearch-6.x
2727
- elasticsearch-7.x
28+
- elasticsearch-java
2829
- fastjson-1.2.x
2930
- feign-default-http-9.x
3031
- feign-pathvar-9.x

0 commit comments

Comments
 (0)