diff --git a/.github/workflows/plugins-jdk17-test.1.yaml b/.github/workflows/plugins-jdk17-test.1.yaml
index 939393713b..ed6efcfef4 100644
--- a/.github/workflows/plugins-jdk17-test.1.yaml
+++ b/.github/workflows/plugins-jdk17-test.1.yaml
@@ -81,6 +81,7 @@ jobs:
- spring-scheduled-6.x-scenario
- caffeine-3.x-scenario
- lettuce-webflux-6x-scenario
+ - elasticsearch-java-9.x-scenario
steps:
- uses: actions/checkout@v2
with:
diff --git a/.github/workflows/plugins-test.2.yaml b/.github/workflows/plugins-test.2.yaml
index 5fe28c244f..1bbf07ef31 100644
--- a/.github/workflows/plugins-test.2.yaml
+++ b/.github/workflows/plugins-test.2.yaml
@@ -97,6 +97,7 @@ jobs:
- nacos-client-2.x-scenario
- rocketmq-scenario
- rocketmq-5-grpc-scenario
+ - elasticsearch-java-scenario
steps:
- uses: actions/checkout@v2
with:
diff --git a/CHANGES.md b/CHANGES.md
index ad1e3f6c51..237ce9ff0c 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -21,6 +21,7 @@ Release Notes.
* Enhance test/plugin/run.sh to support extra Maven properties per version in support-version.list (format: version,key=value).
* Add MariaDB 3.x plugin (all classes renamed in 3.x).
* Extend Jedis 4.x plugin to support Jedis 5.x (fix witness method for 5.x compatibility).
+* Add Elasticsearch Java client (co.elastic.clients:elasticsearch-java) plugin for 7.x-9.x.
All issues and pull requests are [here](https://github.com/apache/skywalking/milestone/249?closed=1)
diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-java-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/elasticsearch-java-plugin/pom.xml
new file mode 100644
index 0000000000..0126147e82
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-java-plugin/pom.xml
@@ -0,0 +1,48 @@
+
+
+
+
+ 4.0.0
+
+
+ apm-sdk-plugin
+ org.apache.skywalking
+ 9.7.0-SNAPSHOT
+
+
+ apm-elasticsearch-java-plugin
+ jar
+
+ elasticsearch-java-plugin
+
+
+ 8.17.0
+
+
+
+
+ co.elastic.clients
+ elasticsearch-java
+ ${elasticsearch-java.version}
+ provided
+
+
+
diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-java-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/java/ElasticsearchPluginConfig.java b/apm-sniffer/apm-sdk-plugin/elasticsearch-java-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/java/ElasticsearchPluginConfig.java
new file mode 100644
index 0000000000..c435f439a8
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-java-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/java/ElasticsearchPluginConfig.java
@@ -0,0 +1,35 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.apm.plugin.elasticsearch.java;
+
+import org.apache.skywalking.apm.agent.core.boot.PluginConfig;
+
+public class ElasticsearchPluginConfig {
+ public static class Plugin {
+ @PluginConfig(root = ElasticsearchPluginConfig.class)
+ public static class Elasticsearch {
+ /**
+ * If true, trace all the DSL(Domain Specific Language) in ElasticSearch access, default is false
+ */
+ public static boolean TRACE_DSL = false;
+
+ public static int ELASTICSEARCH_DSL_LENGTH_THRESHOLD = 1024;
+ }
+ }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-java-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/java/define/ElasticsearchTransportBaseInstrumentation.java b/apm-sniffer/apm-sdk-plugin/elasticsearch-java-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/java/define/ElasticsearchTransportBaseInstrumentation.java
new file mode 100644
index 0000000000..6ac75f023c
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-java-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/java/define/ElasticsearchTransportBaseInstrumentation.java
@@ -0,0 +1,81 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.apm.plugin.elasticsearch.java.define;
+
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
+import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch;
+
+import static net.bytebuddy.matcher.ElementMatchers.named;
+import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName;
+
+/**
+ * Enhance {@code co.elastic.clients.transport.ElasticsearchTransportBase}
+ * which exists in elasticsearch-java 8.x+. The performRequest method
+ * moved from RestClientTransport to this base class in 8.x.
+ *
+ * The peer is propagated from RestClientTransport (subclass) via dynamic field.
+ */
+public class ElasticsearchTransportBaseInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
+
+ private static final String ENHANCE_CLASS = "co.elastic.clients.transport.ElasticsearchTransportBase";
+
+ private static final String PERFORM_REQUEST_INTERCEPTOR =
+ "org.apache.skywalking.apm.plugin.elasticsearch.java.interceptor.TransportPerformRequestInterceptor";
+
+ @Override
+ protected String[] witnessClasses() {
+ return new String[] {ENHANCE_CLASS};
+ }
+
+ @Override
+ protected ClassMatch enhanceClass() {
+ return byName(ENHANCE_CLASS);
+ }
+
+ @Override
+ public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
+ return new ConstructorInterceptPoint[0];
+ }
+
+ @Override
+ public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
+ return new InstanceMethodsInterceptPoint[] {
+ new InstanceMethodsInterceptPoint() {
+ @Override
+ public ElementMatcher getMethodsMatcher() {
+ return named("performRequest");
+ }
+
+ @Override
+ public String getMethodsInterceptor() {
+ return PERFORM_REQUEST_INTERCEPTOR;
+ }
+
+ @Override
+ public boolean isOverrideArgs() {
+ return false;
+ }
+ }
+ };
+ }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-java-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/java/define/RestClientTransportInstrumentation.java b/apm-sniffer/apm-sdk-plugin/elasticsearch-java-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/java/define/RestClientTransportInstrumentation.java
new file mode 100644
index 0000000000..e5dacab15f
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-java-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/java/define/RestClientTransportInstrumentation.java
@@ -0,0 +1,97 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.apm.plugin.elasticsearch.java.define;
+
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
+import org.apache.skywalking.apm.agent.core.plugin.match.ClassMatch;
+
+import static net.bytebuddy.matcher.ElementMatchers.named;
+import static org.apache.skywalking.apm.agent.core.plugin.bytebuddy.ArgumentTypeNameMatch.takesArgumentWithType;
+import static org.apache.skywalking.apm.agent.core.plugin.match.NameMatch.byName;
+
+/**
+ * Enhance {@code co.elastic.clients.transport.rest_client.RestClientTransport}
+ * for the Elasticsearch Java client (co.elastic.clients:elasticsearch-java).
+ *
+ * Covers both 7.x (performRequest on RestClientTransport) and 8.x+
+ * (performRequest inherited from ElasticsearchTransportBase, but constructor on RestClientTransport).
+ */
+public class RestClientTransportInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
+
+ private static final String ENHANCE_CLASS = "co.elastic.clients.transport.rest_client.RestClientTransport";
+
+ private static final String CONSTRUCTOR_INTERCEPTOR =
+ "org.apache.skywalking.apm.plugin.elasticsearch.java.interceptor.RestClientTransportConstructorInterceptor";
+
+ private static final String PERFORM_REQUEST_INTERCEPTOR =
+ "org.apache.skywalking.apm.plugin.elasticsearch.java.interceptor.TransportPerformRequestInterceptor";
+
+ @Override
+ protected String[] witnessClasses() {
+ return new String[] {ENHANCE_CLASS};
+ }
+
+ @Override
+ protected ClassMatch enhanceClass() {
+ return byName(ENHANCE_CLASS);
+ }
+
+ @Override
+ public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
+ return new ConstructorInterceptPoint[] {
+ new ConstructorInterceptPoint() {
+ @Override
+ public ElementMatcher getConstructorMatcher() {
+ return takesArgumentWithType(0, "org.elasticsearch.client.RestClient");
+ }
+
+ @Override
+ public String getConstructorInterceptor() {
+ return CONSTRUCTOR_INTERCEPTOR;
+ }
+ }
+ };
+ }
+
+ @Override
+ public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
+ return new InstanceMethodsInterceptPoint[] {
+ new InstanceMethodsInterceptPoint() {
+ @Override
+ public ElementMatcher getMethodsMatcher() {
+ return named("performRequest");
+ }
+
+ @Override
+ public String getMethodsInterceptor() {
+ return PERFORM_REQUEST_INTERCEPTOR;
+ }
+
+ @Override
+ public boolean isOverrideArgs() {
+ return false;
+ }
+ }
+ };
+ }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-java-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/java/interceptor/RestClientTransportConstructorInterceptor.java b/apm-sniffer/apm-sdk-plugin/elasticsearch-java-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/java/interceptor/RestClientTransportConstructorInterceptor.java
new file mode 100644
index 0000000000..e96932ace3
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-java-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/java/interceptor/RestClientTransportConstructorInterceptor.java
@@ -0,0 +1,43 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.apm.plugin.elasticsearch.java.interceptor;
+
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor;
+import org.elasticsearch.client.Node;
+import org.elasticsearch.client.RestClient;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * Intercept RestClientTransport constructor to extract peer address from RestClient nodes.
+ */
+public class RestClientTransportConstructorInterceptor implements InstanceConstructorInterceptor {
+
+ @Override
+ public void onConstruct(EnhancedInstance objInst, Object[] allArguments) {
+ RestClient restClient = (RestClient) allArguments[0];
+ List nodes = restClient.getNodes();
+ String peers = nodes.stream()
+ .map(node -> node.getHost().toHostString())
+ .collect(Collectors.joining(","));
+ objInst.setSkyWalkingDynamicField(peers);
+ }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-java-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/java/interceptor/TransportPerformRequestInterceptor.java b/apm-sniffer/apm-sdk-plugin/elasticsearch-java-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/java/interceptor/TransportPerformRequestInterceptor.java
new file mode 100644
index 0000000000..ebf8ec6d41
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-java-plugin/src/main/java/org/apache/skywalking/apm/plugin/elasticsearch/java/interceptor/TransportPerformRequestInterceptor.java
@@ -0,0 +1,114 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.apm.plugin.elasticsearch.java.interceptor;
+
+import co.elastic.clients.transport.Endpoint;
+import org.apache.skywalking.apm.agent.core.context.ContextManager;
+import org.apache.skywalking.apm.agent.core.context.tag.Tags;
+import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
+import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
+import org.apache.skywalking.apm.network.trace.component.ComponentsDefine;
+import org.apache.skywalking.apm.plugin.elasticsearch.java.ElasticsearchPluginConfig;
+
+import java.lang.reflect.Method;
+
+/**
+ * Intercept ElasticsearchTransport.performRequest() to create exit spans.
+ *
+ * Args: [0] request, [1] endpoint (Endpoint), [2] options (TransportOptions)
+ * The endpoint.id() provides the operation name (e.g., "search", "index", "bulk").
+ */
+public class TransportPerformRequestInterceptor implements InstanceMethodsAroundInterceptor {
+
+ private static final String DB_TYPE = "Elasticsearch";
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments,
+ Class>[] argumentsTypes, MethodInterceptResult result) throws Throwable {
+ Endpoint endpoint = (Endpoint) allArguments[1];
+ String operationName = "Elasticsearch/" + endpoint.id();
+
+ String peers = (String) objInst.getSkyWalkingDynamicField();
+ if (peers == null || peers.isEmpty()) {
+ peers = "Unknown";
+ }
+
+ AbstractSpan span = ContextManager.createExitSpan(operationName, peers);
+ span.setComponent(ComponentsDefine.REST_HIGH_LEVEL_CLIENT);
+ Tags.DB_TYPE.set(span, DB_TYPE);
+ SpanLayer.asDB(span);
+
+ Object request = allArguments[0];
+ String requestUrl = endpoint.requestUrl(request);
+ String index = extractIndex(requestUrl);
+ if (index != null) {
+ span.tag(Tags.ofKey("db.instance"), index);
+ }
+ if (ElasticsearchPluginConfig.Plugin.Elasticsearch.TRACE_DSL) {
+ String dsl = request.toString();
+ if (dsl != null && !dsl.isEmpty()) {
+ int maxLen = ElasticsearchPluginConfig.Plugin.Elasticsearch.ELASTICSEARCH_DSL_LENGTH_THRESHOLD;
+ if (maxLen > 0 && dsl.length() > maxLen) {
+ dsl = dsl.substring(0, maxLen) + "...";
+ }
+ Tags.DB_STATEMENT.set(span, dsl);
+ }
+ }
+ }
+
+ @Override
+ public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments,
+ Class>[] argumentsTypes, Object ret) throws Throwable {
+ ContextManager.stopSpan();
+ return ret;
+ }
+
+ @Override
+ public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments,
+ Class>[] argumentsTypes, Throwable t) {
+ ContextManager.activeSpan().log(t);
+ ContextManager.activeSpan().errorOccurred();
+ }
+
+ /**
+ * Extract index name from request URL.
+ * E.g., "/test-index/_doc/1" → "test-index", "/_bulk" → null
+ */
+ static String extractIndex(String requestUrl) {
+ if (requestUrl == null || requestUrl.isEmpty()) {
+ return null;
+ }
+ // Remove leading slash
+ String path = requestUrl.startsWith("/") ? requestUrl.substring(1) : requestUrl;
+ if (path.isEmpty()) {
+ return null;
+ }
+ // First segment before '/' or '_' prefix means no index
+ int slashIdx = path.indexOf('/');
+ String firstSegment = slashIdx > 0 ? path.substring(0, slashIdx) : path;
+ if (firstSegment.startsWith("_")) {
+ return null;
+ }
+ return firstSegment;
+ }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/elasticsearch-java-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/elasticsearch-java-plugin/src/main/resources/skywalking-plugin.def
new file mode 100644
index 0000000000..b13b674473
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/elasticsearch-java-plugin/src/main/resources/skywalking-plugin.def
@@ -0,0 +1,19 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Elasticsearch Java client (co.elastic.clients:elasticsearch-java) 7.16.x-9.x
+elasticsearch-java=org.apache.skywalking.apm.plugin.elasticsearch.java.define.RestClientTransportInstrumentation
+elasticsearch-java=org.apache.skywalking.apm.plugin.elasticsearch.java.define.ElasticsearchTransportBaseInstrumentation
diff --git a/apm-sniffer/apm-sdk-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/pom.xml
index 775ec34e12..5d09bd0957 100644
--- a/apm-sniffer/apm-sdk-plugin/pom.xml
+++ b/apm-sniffer/apm-sdk-plugin/pom.xml
@@ -71,6 +71,7 @@
elasticsearch-5.x-pluginelasticsearch-6.x-pluginelasticsearch-7.x-plugin
+ elasticsearch-java-pluginundertow-pluginsrabbitmq-plugindubbo-conflict-patch
diff --git a/docs/en/setup/service-agent/java-agent/Plugin-list.md b/docs/en/setup/service-agent/java-agent/Plugin-list.md
index 2bfd58f872..ed16dc1f85 100644
--- a/docs/en/setup/service-agent/java-agent/Plugin-list.md
+++ b/docs/en/setup/service-agent/java-agent/Plugin-list.md
@@ -25,6 +25,7 @@
- elasticsearch-5.x
- elasticsearch-6.x
- elasticsearch-7.x
+- elasticsearch-java
- fastjson-1.2.x
- feign-default-http-9.x
- feign-pathvar-9.x
diff --git a/docs/en/setup/service-agent/java-agent/Supported-list.md b/docs/en/setup/service-agent/java-agent/Supported-list.md
index 772f169c39..ca6f481f06 100644
--- a/docs/en/setup/service-agent/java-agent/Supported-list.md
+++ b/docs/en/setup/service-agent/java-agent/Supported-list.md
@@ -101,6 +101,7 @@ metrics based on the tracing data.
* [transport-client](https://github.com/elastic/elasticsearch/tree/7.0/client/transport) 7.0.0-7.5.2
* [rest-high-level-client](https://www.elastic.co/guide/en/elasticsearch/client/java-rest/6.7/index.html) 6.7.1-6.8.4
* [rest-high-level-client](https://www.elastic.co/guide/en/elasticsearch/client/java-rest/7.0/java-rest-high.html) 7.x
+ * [elasticsearch-java](https://www.elastic.co/guide/en/elasticsearch/client/java-api-client/current/index.html) 7.17.x-9.x
* [Solr](https://github.com/apache/solr/)
* [SolrJ](https://github.com/apache/solr/tree/main/solr/solrj) 7.x
* [Cassandra](https://github.com/apache/cassandra) 3.x
diff --git a/test/plugin/scenarios/elasticsearch-java-9.x-scenario/bin/startup.sh b/test/plugin/scenarios/elasticsearch-java-9.x-scenario/bin/startup.sh
new file mode 100644
index 0000000000..e054a8dc76
--- /dev/null
+++ b/test/plugin/scenarios/elasticsearch-java-9.x-scenario/bin/startup.sh
@@ -0,0 +1,21 @@
+#!/bin/bash
+#
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+home="$(cd "$(dirname $0)"; pwd)"
+
+java -jar ${agent_opts} -Dskywalking.plugin.elasticsearch.trace_dsl=true ${home}/../libs/elasticsearch-java-9.x-scenario.jar &
diff --git a/test/plugin/scenarios/elasticsearch-java-9.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/elasticsearch-java-9.x-scenario/config/expectedData.yaml
new file mode 100644
index 0000000000..b9af4b71cc
--- /dev/null
+++ b/test/plugin/scenarios/elasticsearch-java-9.x-scenario/config/expectedData.yaml
@@ -0,0 +1,125 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+segmentItems:
+- serviceName: elasticsearch-java-9.x-scenario
+ segmentSize: ge 2
+ segments:
+ - segmentId: not null
+ spans:
+ - operationName: Elasticsearch/es/indices.create
+ parentSpanId: 0
+ spanId: 1
+ spanLayer: Database
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 77
+ isError: false
+ spanType: Exit
+ peer: not null
+ skipAnalysis: false
+ tags:
+ - {key: db.type, value: Elasticsearch}
+ - {key: db.instance, value: test-index}
+ - {key: db.statement, value: not null}
+ - operationName: Elasticsearch/es/index
+ parentSpanId: 0
+ spanId: 2
+ spanLayer: Database
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 77
+ isError: false
+ spanType: Exit
+ peer: not null
+ skipAnalysis: false
+ tags:
+ - {key: db.type, value: Elasticsearch}
+ - {key: db.instance, value: test-index}
+ - {key: db.statement, value: not null}
+ - operationName: Elasticsearch/es/get
+ parentSpanId: 0
+ spanId: 3
+ spanLayer: Database
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 77
+ isError: false
+ spanType: Exit
+ peer: not null
+ skipAnalysis: false
+ tags:
+ - {key: db.type, value: Elasticsearch}
+ - {key: db.instance, value: test-index}
+ - {key: db.statement, value: not null}
+ - operationName: Elasticsearch/es/search
+ parentSpanId: 0
+ spanId: 4
+ spanLayer: Database
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 77
+ isError: false
+ spanType: Exit
+ peer: not null
+ skipAnalysis: false
+ tags:
+ - {key: db.type, value: Elasticsearch}
+ - {key: db.instance, value: test-index}
+ - {key: db.statement, value: not null}
+ - operationName: Elasticsearch/es/delete
+ parentSpanId: 0
+ spanId: 5
+ spanLayer: Database
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 77
+ isError: false
+ spanType: Exit
+ peer: not null
+ skipAnalysis: false
+ tags:
+ - {key: db.type, value: Elasticsearch}
+ - {key: db.instance, value: test-index}
+ - {key: db.statement, value: not null}
+ - operationName: Elasticsearch/es/indices.delete
+ parentSpanId: 0
+ spanId: 6
+ spanLayer: Database
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 77
+ isError: false
+ spanType: Exit
+ peer: not null
+ skipAnalysis: false
+ tags:
+ - {key: db.type, value: Elasticsearch}
+ - {key: db.instance, value: test-index}
+ - {key: db.statement, value: not null}
+ - operationName: GET:/elasticsearch-java-case/case/elasticsearch
+ parentSpanId: -1
+ spanId: 0
+ startTime: nq 0
+ endTime: nq 0
+ spanLayer: Http
+ isError: false
+ spanType: Entry
+ componentId: 1
+ tags:
+ - {key: url, value: not null}
+ - {key: http.method, value: GET}
+ - {key: http.status_code, value: '200'}
+ skipAnalysis: 'false'
diff --git a/test/plugin/scenarios/elasticsearch-java-9.x-scenario/configuration.yml b/test/plugin/scenarios/elasticsearch-java-9.x-scenario/configuration.yml
new file mode 100644
index 0000000000..f660224a0f
--- /dev/null
+++ b/test/plugin/scenarios/elasticsearch-java-9.x-scenario/configuration.yml
@@ -0,0 +1,34 @@
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements. See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership. The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+type: jvm
+entryService: http://localhost:8080/elasticsearch-java-case/case/elasticsearch
+healthCheck: http://localhost:8080/elasticsearch-java-case/case/healthCheck
+startScript: ./bin/startup.sh
+environment:
+- elasticsearch.server=elasticsearch-server:9200
+dependencies:
+ elasticsearch-server:
+ image: docker.elastic.co/elasticsearch/elasticsearch:9.0.0
+ hostname: elasticsearch-server
+ removeOnExit: true
+ expose:
+ - 9200
+ environment:
+ - cluster.name=docker-node
+ - xpack.security.enabled=false
+ - "ES_JAVA_OPTS=-Xms512m -Xmx512m"
+ - discovery.type=single-node
diff --git a/test/plugin/scenarios/elasticsearch-java-9.x-scenario/pom.xml b/test/plugin/scenarios/elasticsearch-java-9.x-scenario/pom.xml
new file mode 100644
index 0000000000..8ca30a4aa0
--- /dev/null
+++ b/test/plugin/scenarios/elasticsearch-java-9.x-scenario/pom.xml
@@ -0,0 +1,138 @@
+
+
+
+
+ org.apache.skywalking.apm.testcase
+ elasticsearch-java-9.x-scenario
+ 1.0.0
+ jar
+
+ 4.0.0
+
+
+ UTF-8
+ 17
+ 3.8.1
+
+ 9.0.0
+
+ 3.0.13
+
+
+ skywalking-elasticsearch-java-9.x-scenario
+
+
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${spring.boot.version}
+ pom
+ import
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-log4j2
+
+
+ jul-to-slf4j
+ org.slf4j
+
+
+
+
+
+ co.elastic.clients
+ elasticsearch-java
+ ${test.framework.version}
+
+
+
+ org.elasticsearch.client
+ elasticsearch-rest-client
+ ${test.framework.version}
+
+
+ com.fasterxml.jackson.core
+ jackson-databind
+
+
+
+
+ elasticsearch-java-9.x-scenario
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+ ${spring.boot.version}
+
+
+
+ repackage
+
+
+
+
+
+ maven-compiler-plugin
+ ${maven-compiler-plugin.version}
+
+ ${compiler.version}
+ ${compiler.version}
+ ${project.build.sourceEncoding}
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+ assemble
+ package
+
+ single
+
+
+
+ src/main/assembly/assembly.xml
+
+ ./target/
+
+
+
+
+
+
+
diff --git a/test/plugin/scenarios/elasticsearch-java-9.x-scenario/src/main/assembly/assembly.xml b/test/plugin/scenarios/elasticsearch-java-9.x-scenario/src/main/assembly/assembly.xml
new file mode 100644
index 0000000000..42a1323949
--- /dev/null
+++ b/test/plugin/scenarios/elasticsearch-java-9.x-scenario/src/main/assembly/assembly.xml
@@ -0,0 +1,41 @@
+
+
+
+
+ zip
+
+
+
+
+ ./bin
+ 0775
+
+
+
+
+
+ ${project.build.directory}/elasticsearch-java-9.x-scenario.jar
+ ./libs
+ 0775
+
+
+
diff --git a/test/plugin/scenarios/elasticsearch-java-9.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/elasticsearch/Application.java b/test/plugin/scenarios/elasticsearch-java-9.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/elasticsearch/Application.java
new file mode 100644
index 0000000000..3ce49f987a
--- /dev/null
+++ b/test/plugin/scenarios/elasticsearch-java-9.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/elasticsearch/Application.java
@@ -0,0 +1,30 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.apm.testcase.elasticsearch;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.autoconfigure.elasticsearch.ElasticsearchClientAutoConfiguration;
+
+@SpringBootApplication(exclude = ElasticsearchClientAutoConfiguration.class)
+public class Application {
+ public static void main(String[] args) {
+ SpringApplication.run(Application.class, args);
+ }
+}
diff --git a/test/plugin/scenarios/elasticsearch-java-9.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/elasticsearch/controller/CaseController.java b/test/plugin/scenarios/elasticsearch-java-9.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/elasticsearch/controller/CaseController.java
new file mode 100644
index 0000000000..3f2be16239
--- /dev/null
+++ b/test/plugin/scenarios/elasticsearch-java-9.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/elasticsearch/controller/CaseController.java
@@ -0,0 +1,96 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.skywalking.apm.testcase.elasticsearch.controller;
+
+import co.elastic.clients.elasticsearch.ElasticsearchClient;
+import co.elastic.clients.elasticsearch.core.IndexResponse;
+import co.elastic.clients.elasticsearch.core.GetResponse;
+import co.elastic.clients.elasticsearch.core.SearchResponse;
+import co.elastic.clients.elasticsearch.core.DeleteResponse;
+import co.elastic.clients.elasticsearch.indices.CreateIndexResponse;
+import co.elastic.clients.elasticsearch.indices.DeleteIndexResponse;
+import co.elastic.clients.json.jackson.JacksonJsonpMapper;
+import co.elastic.clients.transport.rest_client.RestClientTransport;
+import org.apache.http.HttpHost;
+import org.elasticsearch.client.RestClient;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+@RestController
+@RequestMapping("/elasticsearch-java-case/case")
+public class CaseController {
+
+ @Value("${elasticsearch.server}")
+ private String elasticsearchServer;
+
+ @GetMapping("/healthCheck")
+ public String healthCheck() throws IOException {
+ RestClient restClient = RestClient.builder(HttpHost.create(elasticsearchServer)).build();
+ RestClientTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
+ ElasticsearchClient client = new ElasticsearchClient(transport);
+ try {
+ client.info();
+ return "Success";
+ } finally {
+ restClient.close();
+ }
+ }
+
+ @GetMapping("/elasticsearch")
+ public String elasticsearch() throws IOException {
+ RestClient restClient = RestClient.builder(HttpHost.create(elasticsearchServer)).build();
+ RestClientTransport transport = new RestClientTransport(restClient, new JacksonJsonpMapper());
+ ElasticsearchClient client = new ElasticsearchClient(transport);
+ try {
+
+ // Create index
+ CreateIndexResponse createResp = client.indices().create(c -> c.index("test-index"));
+
+ // Index document
+ Map doc = new HashMap<>();
+ doc.put("name", "test");
+ doc.put("value", "skywalking");
+ IndexResponse indexResp = client.index(i -> i.index("test-index").id("1").document(doc));
+
+ // Get document
+ GetResponse