diff --git a/.github/workflows/plugins-test.1.yaml b/.github/workflows/plugins-test.1.yaml
index 2b4e1fd17e..f6adf64bfc 100644
--- a/.github/workflows/plugins-test.1.yaml
+++ b/.github/workflows/plugins-test.1.yaml
@@ -47,6 +47,7 @@ jobs:
- { name: 'kotlin-coroutine-scenario', title: 'Kotlin Coroutine 1.0.1-1.3.3 (4)' }
- { name: 'lettuce-scenario', title: 'Lettuce 5.x (17)' }
- { name: 'mongodb-3.x-scenario', title: 'Mongodb 3.4.0-3.11.1 (22)' }
+ - { name: 'mongodb-4.x-scenario', title: 'Mongodb 4.0.0-4.1.0 (7)' }
- { name: 'netty-socketio-scenario', title: 'Netty-SocketIO 1.x (4)' }
- { name: 'postgresql-above9.4.1207-scenario', title: 'PostgreSQL 9.4.1207+ (62)' }
steps:
diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/pom.xml
new file mode 100644
index 0000000000..b2ca1c98a7
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/pom.xml
@@ -0,0 +1,46 @@
+
+
+
+ 4.0.0
+
+ apm-sdk-plugin
+ org.apache.skywalking
+ 8.2.0-SNAPSHOT
+
+
+ apm-mongodb-4.x-plugin
+ jar
+
+ mongodb-plugin
+ http://maven.apache.org
+
+
+ UTF-8
+ 4.1.0
+
+
+
+
+ org.mongodb
+ mongodb-driver-sync
+ ${mongodb-driver.version}
+ provided
+
+
+
diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/define/MongoDBClientDelegateInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/define/MongoDBClientDelegateInstrumentation.java
new file mode 100644
index 0000000000..6967847593
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/define/MongoDBClientDelegateInstrumentation.java
@@ -0,0 +1,100 @@
+/*
+ * 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.mongodb.v4.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 org.apache.skywalking.apm.plugin.mongodb.v4.interceptor.MongoDBClientDelegateInterceptor;
+
+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 com.mongodb.client.internal.MongoClientDelegate} instance, and intercept {@code
+ * com.mongodb.client.internal.MongoClientDelegate#getOperationExecutor()}, this is the only way to get
+ * OperationExecutor which is unified entrance of execute mongo command. Inject the remotePeer into enhanced OperationExecutor.
+ *
+ * support: 4.0.0 or higher
+ *
+ * @see MongoDBOperationExecutorInstrumentation
+ * @see MongoDBClientDelegateInterceptor
+ */
+public class MongoDBClientDelegateInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
+
+ private static final String WITNESS_CLASS = "com.mongodb.internal.connection.Cluster";
+
+ private static final String ENHANCE_CLASS = "com.mongodb.client.internal.MongoClientDelegate";
+
+ private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v4.interceptor.MongoDBClientDelegateInterceptor";
+
+ @Override
+ protected String[] witnessClasses() {
+ return new String[] {WITNESS_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, "com.mongodb.internal.connection.Cluster");
+ }
+
+ @Override
+ public String getConstructorInterceptor() {
+ return INTERCEPTOR_CLASS;
+ }
+ }
+ };
+ }
+
+ @Override
+ public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
+ return new InstanceMethodsInterceptPoint[] {
+ new InstanceMethodsInterceptPoint() {
+ @Override
+ public ElementMatcher getMethodsMatcher() {
+ return named("getOperationExecutor");
+ }
+
+ @Override
+ public String getMethodsInterceptor() {
+ return INTERCEPTOR_CLASS;
+ }
+
+ @Override
+ public boolean isOverrideArgs() {
+ return false;
+ }
+ }
+ };
+ }
+
+}
diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/define/MongoDBOperationExecutorInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/define/MongoDBOperationExecutorInstrumentation.java
new file mode 100644
index 0000000000..76107836a2
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/define/MongoDBOperationExecutorInstrumentation.java
@@ -0,0 +1,87 @@
+/*
+ * 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.mongodb.v4.define;
+
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+import net.bytebuddy.matcher.ElementMatchers;
+import org.apache.skywalking.apm.agent.core.plugin.bytebuddy.ArgumentTypeNameMatch;
+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 org.apache.skywalking.apm.agent.core.plugin.match.NameMatch;
+
+/**
+ * same with org.apache.skywalking.apm.plugin.mongodb.v3.define.v38.MongoDBOperationExecutorInstrumentation
+ *
+ * support: 4.0.x or higher
+ */
+public class MongoDBOperationExecutorInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
+
+ private static final String WITNESS_CLASS = "com.mongodb.internal.operation.FindOperation";
+
+ private static final String ENHANCE_CLASS = "com.mongodb.client.internal.MongoClientDelegate$DelegateOperationExecutor";
+
+ private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v4.interceptor.MongoDBOperationExecutorInterceptor";
+
+ private static final String METHOD_NAME = "execute";
+
+ private static final String ARGUMENT_TYPE = "com.mongodb.client.ClientSession";
+
+ @Override
+ protected String[] witnessClasses() {
+ return new String[] {WITNESS_CLASS};
+ }
+
+ @Override
+ protected ClassMatch enhanceClass() {
+ return NameMatch.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 ElementMatchers
+ .named(METHOD_NAME)
+ .and(ArgumentTypeNameMatch.takesArgumentWithType(2, ARGUMENT_TYPE))
+ .or(ElementMatchers.named(METHOD_NAME).and(ArgumentTypeNameMatch.takesArgumentWithType(3, ARGUMENT_TYPE)));
+ }
+
+ @Override
+ public String getMethodsInterceptor() {
+ return INTERCEPTOR_CLASS;
+ }
+
+ @Override
+ public boolean isOverrideArgs() {
+ return false;
+ }
+ }
+ };
+ }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/interceptor/MongoDBClientDelegateInterceptor.java b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/interceptor/MongoDBClientDelegateInterceptor.java
new file mode 100644
index 0000000000..932bbea008
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/interceptor/MongoDBClientDelegateInterceptor.java
@@ -0,0 +1,70 @@
+/*
+ * 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.mongodb.v4.interceptor;
+
+import com.mongodb.internal.connection.Cluster;
+import org.apache.skywalking.apm.agent.core.logging.api.ILog;
+import org.apache.skywalking.apm.agent.core.logging.api.LogManager;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor;
+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.plugin.mongodb.v4.support.MongoRemotePeerHelper;
+
+import java.lang.reflect.Method;
+
+public class MongoDBClientDelegateInterceptor implements InstanceConstructorInterceptor, InstanceMethodsAroundInterceptor {
+
+ private static final ILog LOGGER = LogManager.getLogger(MongoDBClientDelegateInterceptor.class);
+
+ @Override
+ public void onConstruct(EnhancedInstance objInst, Object[] allArguments) {
+ Cluster cluster = (Cluster) allArguments[0];
+ String remotePeer = MongoRemotePeerHelper.getRemotePeer(cluster);
+ objInst.setSkyWalkingDynamicField(remotePeer);
+ }
+
+ @Override
+ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class>[] argumentsTypes,
+ MethodInterceptResult result) {
+ // do nothing
+ }
+
+ @Override
+ public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class>[] argumentsTypes,
+ Object ret) {
+ if (ret instanceof EnhancedInstance) {
+ // pass remotePeer to OperationExecutor, which has be enhanced as EnhancedInstance
+ // See: org.apache.skywalking.apm.plugin.mongodb.v3.define.v37.MongoDBOperationExecutorInstrumentation
+ EnhancedInstance retInstance = (EnhancedInstance) ret;
+ String remotePeer = (String) objInst.getSkyWalkingDynamicField();
+ if (LOGGER.isDebugEnable()) {
+ LOGGER.debug("Mark OperationExecutor remotePeer: {}", remotePeer);
+ }
+ retInstance.setSkyWalkingDynamicField(remotePeer);
+ }
+ return ret;
+ }
+
+ @Override
+ public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments,
+ Class>[] argumentsTypes, Throwable t) {
+ // do nothing
+ }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/interceptor/MongoDBOperationExecutorInterceptor.java b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/interceptor/MongoDBOperationExecutorInterceptor.java
new file mode 100644
index 0000000000..dbdf7c6926
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/interceptor/MongoDBOperationExecutorInterceptor.java
@@ -0,0 +1,64 @@
+/*
+ * 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.mongodb.v4.interceptor;
+
+import org.apache.skywalking.apm.agent.core.context.ContextManager;
+import org.apache.skywalking.apm.agent.core.context.trace.AbstractSpan;
+import org.apache.skywalking.apm.agent.core.logging.api.ILog;
+import org.apache.skywalking.apm.agent.core.logging.api.LogManager;
+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.plugin.mongodb.v4.support.MongoSpanHelper;
+
+import java.lang.reflect.Method;
+
+@SuppressWarnings("Duplicates")
+public class MongoDBOperationExecutorInterceptor implements InstanceMethodsAroundInterceptor {
+
+ private static final ILog LOGGER = LogManager.getLogger(MongoDBOperationExecutorInterceptor.class);
+
+ @Override
+ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class>[] argumentsTypes,
+ MethodInterceptResult result) {
+ String executeMethod = allArguments[0].getClass().getSimpleName();
+ // OperationExecutor has included th remotePeer
+ // See: MongoDBClientDelegateInterceptor.afterMethod
+ String remotePeer = (String) objInst.getSkyWalkingDynamicField();
+ if (LOGGER.isDebugEnable()) {
+ LOGGER.debug("Mongo execute: [executeMethod: {}, remotePeer: {}]", executeMethod, remotePeer);
+ }
+ MongoSpanHelper.createExitSpan(executeMethod, remotePeer, allArguments[0]);
+ }
+
+ @Override
+ public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class>[] argumentsTypes,
+ Object ret) {
+ ContextManager.stopSpan();
+ return ret;
+ }
+
+ @Override
+ public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments,
+ Class>[] argumentsTypes, Throwable t) {
+ AbstractSpan activeSpan = ContextManager.activeSpan();
+ activeSpan.log(t);
+ }
+
+}
diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/support/MongoConstants.java b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/support/MongoConstants.java
new file mode 100644
index 0000000000..5c8da40b8d
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/support/MongoConstants.java
@@ -0,0 +1,32 @@
+/*
+ * 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.mongodb.v4.support;
+
+public class MongoConstants {
+
+ private MongoConstants() {
+ }
+
+ public static final String DB_TYPE = "MongoDB";
+
+ public static final String MONGO_DB_OP_PREFIX = "MongoDB/";
+
+ public static final String EMPTY = "";
+
+}
diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/support/MongoOperationHelper.java b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/support/MongoOperationHelper.java
new file mode 100644
index 0000000000..45d0481fbc
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/support/MongoOperationHelper.java
@@ -0,0 +1,143 @@
+/*
+ * 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.mongodb.v4.support;
+
+import com.mongodb.internal.bulk.DeleteRequest;
+import com.mongodb.internal.bulk.InsertRequest;
+import com.mongodb.internal.bulk.UpdateRequest;
+import com.mongodb.internal.bulk.WriteRequest;
+import com.mongodb.internal.operation.CountOperation;
+import com.mongodb.internal.operation.CreateCollectionOperation;
+import com.mongodb.internal.operation.CreateIndexesOperation;
+import com.mongodb.internal.operation.CreateViewOperation;
+import com.mongodb.internal.operation.DeleteOperation;
+import com.mongodb.internal.operation.DistinctOperation;
+import com.mongodb.internal.operation.FindAndDeleteOperation;
+import com.mongodb.internal.operation.FindAndReplaceOperation;
+import com.mongodb.internal.operation.FindAndUpdateOperation;
+import com.mongodb.internal.operation.FindOperation;
+import com.mongodb.internal.operation.InsertOperation;
+import com.mongodb.internal.operation.ListCollectionsOperation;
+import com.mongodb.internal.operation.MapReduceToCollectionOperation;
+import com.mongodb.internal.operation.MapReduceWithInlineResultsOperation;
+import com.mongodb.internal.operation.MixedBulkWriteOperation;
+import com.mongodb.internal.operation.UpdateOperation;
+import org.bson.BsonDocument;
+
+import java.util.List;
+
+@SuppressWarnings({
+ "deprecation",
+ "Duplicates"
+})
+public class MongoOperationHelper {
+
+ private MongoOperationHelper() {
+
+ }
+
+ /**
+ * Convert ReadOperation interface or WriteOperation interface to the implementation class. Get the method name and
+ * filter info.
+ * @param obj operation
+ * @return result
+ */
+ public static String getTraceParam(Object obj) {
+ if (obj instanceof CountOperation) {
+ BsonDocument filter = ((CountOperation) obj).getFilter();
+ return limitFilter(filter.toString());
+ } else if (obj instanceof DistinctOperation) {
+ BsonDocument filter = ((DistinctOperation) obj).getFilter();
+ return limitFilter(filter.toString());
+ } else if (obj instanceof FindOperation) {
+ BsonDocument filter = ((FindOperation) obj).getFilter();
+ return limitFilter(filter.toString());
+ } else if (obj instanceof ListCollectionsOperation) {
+ BsonDocument filter = ((ListCollectionsOperation) obj).getFilter();
+ return limitFilter(filter.toString());
+ } else if (obj instanceof MapReduceWithInlineResultsOperation) {
+ BsonDocument filter = ((MapReduceWithInlineResultsOperation) obj).getFilter();
+ return limitFilter(filter.toString());
+ } else if (obj instanceof DeleteOperation) {
+ List writeRequestList = ((DeleteOperation) obj).getDeleteRequests();
+ return getFilter(writeRequestList);
+ } else if (obj instanceof InsertOperation) {
+ List writeRequestList = ((InsertOperation) obj).getInsertRequests();
+ return getFilter(writeRequestList);
+ } else if (obj instanceof UpdateOperation) {
+ List writeRequestList = ((UpdateOperation) obj).getUpdateRequests();
+ return getFilter(writeRequestList);
+ } else if (obj instanceof CreateCollectionOperation) {
+ String filter = ((CreateCollectionOperation) obj).getCollectionName();
+ return limitFilter(filter);
+ } else if (obj instanceof CreateIndexesOperation) {
+ List filter = ((CreateIndexesOperation) obj).getIndexNames();
+ return limitFilter(filter.toString());
+ } else if (obj instanceof CreateViewOperation) {
+ String filter = ((CreateViewOperation) obj).getViewName();
+ return limitFilter(filter);
+ } else if (obj instanceof FindAndDeleteOperation) {
+ BsonDocument filter = ((FindAndDeleteOperation) obj).getFilter();
+ return limitFilter(filter.toString());
+ } else if (obj instanceof FindAndReplaceOperation) {
+ BsonDocument filter = ((FindAndReplaceOperation) obj).getFilter();
+ return limitFilter(filter.toString());
+ } else if (obj instanceof FindAndUpdateOperation) {
+ BsonDocument filter = ((FindAndUpdateOperation) obj).getFilter();
+ return limitFilter(filter.toString());
+ } else if (obj instanceof MapReduceToCollectionOperation) {
+ BsonDocument filter = ((MapReduceToCollectionOperation) obj).getFilter();
+ return limitFilter(filter.toString());
+ } else if (obj instanceof MixedBulkWriteOperation) {
+ List extends WriteRequest> writeRequestList = ((MixedBulkWriteOperation) obj).getWriteRequests();
+ return getFilter(writeRequestList);
+ } else {
+ return MongoConstants.EMPTY;
+ }
+ }
+
+ private static String getFilter(List extends WriteRequest> writeRequestList) {
+ StringBuilder params = new StringBuilder();
+ for (WriteRequest request : writeRequestList) {
+ if (request instanceof InsertRequest) {
+ params.append(((InsertRequest) request).getDocument().toString()).append(",");
+ } else if (request instanceof DeleteRequest) {
+ params.append(((DeleteRequest) request).getFilter()).append(",");
+ } else if (request instanceof UpdateRequest) {
+ params.append(((UpdateRequest) request).getFilter()).append(",");
+ }
+ final int filterLengthLimit = MongoPluginConfig.Plugin.MongoDB.FILTER_LENGTH_LIMIT;
+ if (filterLengthLimit > 0 && params.length() > filterLengthLimit) {
+ return params.substring(0, filterLengthLimit) + "...";
+ }
+ }
+ return params.toString();
+ }
+
+ private static String limitFilter(String filter) {
+ final StringBuilder params = new StringBuilder();
+ final int filterLengthLimit = MongoPluginConfig.Plugin.MongoDB.FILTER_LENGTH_LIMIT;
+ if (filterLengthLimit > 0 && filter.length() > filterLengthLimit) {
+ return params.append(filter, 0, filterLengthLimit).append("...").toString();
+ } else {
+ return filter;
+ }
+ }
+
+}
diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/support/MongoPluginConfig.java b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/support/MongoPluginConfig.java
new file mode 100644
index 0000000000..81f55852bb
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/support/MongoPluginConfig.java
@@ -0,0 +1,42 @@
+/*
+ * 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.mongodb.v4.support;
+
+import org.apache.skywalking.apm.agent.core.boot.PluginConfig;
+
+public class MongoPluginConfig {
+ public static class Plugin {
+ @PluginConfig(root = MongoPluginConfig.class)
+ public static class MongoDB {
+ /**
+ * If true, trace all the parameters in MongoDB access, default is false. Only trace the operation, not
+ * include parameters.
+ */
+ public static boolean TRACE_PARAM = false;
+
+ /**
+ * For the sake of performance, SkyWalking won't save the entire parameters string into the tag, but only
+ * the first {@code FILTER_LENGTH_LIMIT} characters.
+ *
+ * Set a negative number to save the complete parameter string to the tag.
+ */
+ public static int FILTER_LENGTH_LIMIT = 256;
+ }
+ }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/support/MongoRemotePeerHelper.java b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/support/MongoRemotePeerHelper.java
new file mode 100644
index 0000000000..1dcdb288f8
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/support/MongoRemotePeerHelper.java
@@ -0,0 +1,44 @@
+/*
+ * 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.mongodb.v4.support;
+
+import com.mongodb.ServerAddress;
+import com.mongodb.connection.ServerDescription;
+import com.mongodb.internal.connection.Cluster;
+
+@SuppressWarnings("deprecation")
+public class MongoRemotePeerHelper {
+
+ private MongoRemotePeerHelper() {
+ }
+
+ /**
+ *
+ * @param cluster cluster
+ * @return result
+ */
+ public static String getRemotePeer(Cluster cluster) {
+ StringBuilder peersBuilder = new StringBuilder();
+ for (ServerDescription description : cluster.getDescription().getServerDescriptions()) {
+ ServerAddress address = description.getAddress();
+ peersBuilder.append(address.getHost()).append(":").append(address.getPort()).append(";");
+ }
+ return peersBuilder.substring(0, peersBuilder.length() - 1);
+ }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/support/MongoSpanHelper.java b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/support/MongoSpanHelper.java
new file mode 100644
index 0000000000..c01953b0c4
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v4/support/MongoSpanHelper.java
@@ -0,0 +1,50 @@
+/*
+ * 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.mongodb.v4.support;
+
+import org.apache.skywalking.apm.agent.core.context.ContextCarrier;
+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.network.trace.component.ComponentsDefine;
+
+public class MongoSpanHelper {
+
+ private MongoSpanHelper() {
+ }
+
+ /**
+ * createExitSpan
+ * @param executeMethod executeMethod
+ * @param remotePeer remotePeer
+ * @param operation operation
+ */
+ public static void createExitSpan(String executeMethod, String remotePeer, Object operation) {
+ AbstractSpan span = ContextManager.createExitSpan(
+ MongoConstants.MONGO_DB_OP_PREFIX + executeMethod, new ContextCarrier(), remotePeer);
+ span.setComponent(ComponentsDefine.MONGO_DRIVER);
+ Tags.DB_TYPE.set(span, MongoConstants.DB_TYPE);
+ SpanLayer.asDB(span);
+
+ if (MongoPluginConfig.Plugin.MongoDB.TRACE_PARAM) {
+ Tags.DB_BIND_VARIABLES.set(span, MongoOperationHelper.getTraceParam(operation));
+ }
+ }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/main/resources/skywalking-plugin.def
new file mode 100644
index 0000000000..a019f7c913
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-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.
+
+# v4.0.0~
+mongodb-4.x=org.apache.skywalking.apm.plugin.mongodb.v4.define.MongoDBClientDelegateInstrumentation
+mongodb-4.x=org.apache.skywalking.apm.plugin.mongodb.v4.define.MongoDBOperationExecutorInstrumentation
diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/mongodb/v4/MongoDBClientDelegateInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/mongodb/v4/MongoDBClientDelegateInterceptorTest.java
new file mode 100644
index 0000000000..16c2e31ed5
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/mongodb/v4/MongoDBClientDelegateInterceptorTest.java
@@ -0,0 +1,72 @@
+/*
+ * 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.mongodb.v4;
+
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import org.apache.skywalking.apm.plugin.mongodb.v4.interceptor.MongoDBClientDelegateInterceptor;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.powermock.modules.junit4.PowerMockRunner;
+
+import static org.mockito.Mockito.when;
+
+@RunWith(PowerMockRunner.class)
+public class MongoDBClientDelegateInterceptorTest {
+
+ private MongoDBClientDelegateInterceptor interceptor;
+
+ @Mock
+ private EnhancedInstance clientDelegateEnhancedInstance;
+
+ private EnhancedInstance retEnhancedInstance;
+
+ private final static String REMOTE_PEER = "127.0.0.1:27017";
+
+ @Before
+ public void setUp() {
+ interceptor = new MongoDBClientDelegateInterceptor();
+ retEnhancedInstance = new FieldEnhancedInstance();
+ when(clientDelegateEnhancedInstance.getSkyWalkingDynamicField()).thenReturn(REMOTE_PEER);
+ }
+
+ @Test
+ public void testAfterMethod() {
+ interceptor.afterMethod(clientDelegateEnhancedInstance, null, null, null, retEnhancedInstance);
+ Assert.assertEquals(REMOTE_PEER, retEnhancedInstance.getSkyWalkingDynamicField());
+ }
+
+ private static class FieldEnhancedInstance implements EnhancedInstance {
+
+ private Object skyWalkingDynamicField;
+
+ @Override
+ public Object getSkyWalkingDynamicField() {
+ return skyWalkingDynamicField;
+ }
+
+ @Override
+ public void setSkyWalkingDynamicField(Object value) {
+ this.skyWalkingDynamicField = value;
+ }
+ }
+
+}
diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/mongodb/v4/MongoDBOperationExecutorInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/mongodb/v4/MongoDBOperationExecutorInterceptorTest.java
new file mode 100644
index 0000000000..d5dc3e57f5
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/mongodb-4.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/mongodb/v4/MongoDBOperationExecutorInterceptorTest.java
@@ -0,0 +1,141 @@
+/*
+ * 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.mongodb.v4;
+
+import com.mongodb.MongoNamespace;
+import com.mongodb.ReadConcern;
+import com.mongodb.client.internal.OperationExecutor;
+import com.mongodb.internal.operation.FindOperation;
+import com.mongodb.internal.operation.WriteOperation;
+import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan;
+import org.apache.skywalking.apm.agent.core.context.trace.LogDataEntity;
+import org.apache.skywalking.apm.agent.core.context.trace.SpanLayer;
+import org.apache.skywalking.apm.agent.core.context.trace.TraceSegment;
+import org.apache.skywalking.apm.agent.core.context.util.TagValuePair;
+import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import org.apache.skywalking.apm.agent.test.helper.SegmentHelper;
+import org.apache.skywalking.apm.agent.test.helper.SpanHelper;
+import org.apache.skywalking.apm.agent.test.tools.AgentServiceRule;
+import org.apache.skywalking.apm.agent.test.tools.SegmentStorage;
+import org.apache.skywalking.apm.agent.test.tools.SegmentStoragePoint;
+import org.apache.skywalking.apm.agent.test.tools.SpanAssert;
+import org.apache.skywalking.apm.agent.test.tools.TracingSegmentRunner;
+import org.apache.skywalking.apm.plugin.mongodb.v4.interceptor.MongoDBOperationExecutorInterceptor;
+import org.apache.skywalking.apm.plugin.mongodb.v4.support.MongoPluginConfig;
+import org.bson.BsonDocument;
+import org.bson.BsonString;
+import org.bson.codecs.Decoder;
+import org.hamcrest.CoreMatchers;
+import org.hamcrest.MatcherAssert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.powermock.api.mockito.PowerMockito;
+import org.powermock.modules.junit4.PowerMockRunner;
+import org.powermock.modules.junit4.PowerMockRunnerDelegate;
+
+import java.lang.reflect.Method;
+import java.util.List;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertThat;
+import static org.mockito.Mockito.when;
+
+@RunWith(PowerMockRunner.class)
+@PowerMockRunnerDelegate(TracingSegmentRunner.class)
+public class MongoDBOperationExecutorInterceptorTest {
+
+ @SegmentStoragePoint
+ private SegmentStorage segmentStorage;
+
+ @Rule
+ public AgentServiceRule serviceRule = new AgentServiceRule();
+
+ @Mock
+ private EnhancedInstance enhancedInstance;
+
+ private MongoDBOperationExecutorInterceptor interceptor;
+
+ private Object[] arguments;
+
+ private Class[] argumentTypes;
+
+ @Before
+ public void setUp() {
+
+ interceptor = new MongoDBOperationExecutorInterceptor();
+
+ MongoPluginConfig.Plugin.MongoDB.TRACE_PARAM = true;
+
+ when(enhancedInstance.getSkyWalkingDynamicField()).thenReturn("127.0.0.1:27017");
+
+ BsonDocument document = new BsonDocument();
+ document.append("name", new BsonString("by"));
+ MongoNamespace mongoNamespace = new MongoNamespace("test.user");
+ Decoder decoder = PowerMockito.mock(Decoder.class);
+ FindOperation findOperation = new FindOperation(mongoNamespace, decoder);
+ findOperation.filter(document);
+
+ arguments = new Object[] {findOperation};
+ argumentTypes = new Class[] {findOperation.getClass()};
+ }
+
+ @Test
+ public void testIntercept() throws Throwable {
+ interceptor.beforeMethod(enhancedInstance, getMethod(), arguments, argumentTypes, null);
+ interceptor.afterMethod(enhancedInstance, getMethod(), arguments, argumentTypes, null);
+
+ MatcherAssert.assertThat(segmentStorage.getTraceSegments().size(), is(1));
+ TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0);
+ List spans = SegmentHelper.getSpans(traceSegment);
+ assertMongoSpan(spans.get(0));
+ }
+
+ @Test
+ public void testInterceptWithException() throws Throwable {
+ interceptor.beforeMethod(enhancedInstance, getMethod(), arguments, argumentTypes, null);
+ interceptor.handleMethodException(
+ enhancedInstance, getMethod(), arguments, argumentTypes, new RuntimeException());
+ interceptor.afterMethod(enhancedInstance, getMethod(), arguments, argumentTypes, null);
+
+ MatcherAssert.assertThat(segmentStorage.getTraceSegments().size(), is(1));
+ TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0);
+ List spans = SegmentHelper.getSpans(traceSegment);
+ assertMongoSpan(spans.get(0));
+ List logDataEntities = SpanHelper.getLogs(spans.get(0));
+ assertThat(logDataEntities.size(), is(1));
+ SpanAssert.assertException(logDataEntities.get(0), RuntimeException.class);
+ }
+
+ private void assertMongoSpan(AbstractTracingSpan span) {
+ assertThat(span.getOperationName(), is("MongoDB/FindOperation"));
+ assertThat(SpanHelper.getComponentId(span), is(42));
+ List tags = SpanHelper.getTags(span);
+ assertThat(tags.get(1).getValue(), is("{\"name\": \"by\"}"));
+ assertThat(tags.get(0).getValue(), is("MongoDB"));
+ assertThat(span.isExit(), is(true));
+ assertThat(SpanHelper.getLayer(span), CoreMatchers.is(SpanLayer.DB));
+ }
+
+ private Method getMethod() throws Exception {
+ return OperationExecutor.class.getMethod("execute", WriteOperation.class, ReadConcern.class);
+ }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/pom.xml
index a28cc63344..78e66961e5 100644
--- a/apm-sniffer/apm-sdk-plugin/pom.xml
+++ b/apm-sniffer/apm-sdk-plugin/pom.xml
@@ -36,6 +36,7 @@
tomcat-7.x-8.x-pluginmotan-pluginmongodb-3.x-plugin
+ mongodb-4.x-pluginfeign-default-http-9.x-pluginokhttp-3.x-pluginspring-plugins
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 b8974e5fa1..4d4c9fcd98 100644
--- a/docs/en/setup/service-agent/java-agent/Plugin-list.md
+++ b/docs/en/setup/service-agent/java-agent/Plugin-list.md
@@ -41,6 +41,7 @@
- memcache-2.x
- mongodb-2.x
- mongodb-3.x
+- mongodb-4.x
- motan-0.x
- mysql-5.x
- mysql-6.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 b37f853e3e..bac5bd4490 100644
--- a/docs/en/setup/service-agent/java-agent/Supported-list.md
+++ b/docs/en/setup/service-agent/java-agent/Supported-list.md
@@ -58,7 +58,7 @@
* [Jedis](https://github.com/xetorthio/jedis) 2.x
* [Redisson](https://github.com/redisson/redisson) Easy Java Redis client 3.5.2+
* [Lettuce](https://github.com/lettuce-io/lettuce-core) 5.x
- * [MongoDB Java Driver](https://github.com/mongodb/mongo-java-driver) 2.13-2.14, 3.4.0-3.11.1
+ * [MongoDB Java Driver](https://github.com/mongodb/mongo-java-driver) 2.13-2.14, 3.4.0-3.11.1, 4.0.0-4.1.0
* Memcached Client
* [Spymemcached](https://github.com/couchbase/spymemcached) 2.x
* [Xmemcached](https://github.com/killme2008/xmemcached) 2.x
diff --git a/test/plugin/scenarios/mongodb-4.x-scenario/bin/startup.sh b/test/plugin/scenarios/mongodb-4.x-scenario/bin/startup.sh
new file mode 100644
index 0000000000..df07bc1934
--- /dev/null
+++ b/test/plugin/scenarios/mongodb-4.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.mongodb.trace_param=true ${home}/../libs/mongodb-4.x-scenario.jar &
\ No newline at end of file
diff --git a/test/plugin/scenarios/mongodb-4.x-scenario/config/expectedData.yaml b/test/plugin/scenarios/mongodb-4.x-scenario/config/expectedData.yaml
new file mode 100644
index 0000000000..78d77b411e
--- /dev/null
+++ b/test/plugin/scenarios/mongodb-4.x-scenario/config/expectedData.yaml
@@ -0,0 +1,141 @@
+# 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: mongodb-4.x-scenario
+ segmentSize: ge 2
+ segments:
+ - segmentId: not null
+ spans:
+ - operationName: MongoDB/CreateCollectionOperation
+ operationId: 0
+ parentSpanId: 0
+ spanId: 1
+ spanLayer: Database
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 42
+ isError: false
+ spanType: Exit
+ peer: mongodb-server:27017
+ tags:
+ - {key: db.type, value: MongoDB}
+ - {key: db.bind_vars, value: 'testCollection'}
+ skipAnalysis: 'false'
+ - operationName: MongoDB/MixedBulkWriteOperation
+ operationId: 0
+ parentSpanId: 0
+ spanId: 2
+ spanLayer: Database
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 42
+ isError: false
+ spanType: Exit
+ peer: mongodb-server:27017
+ tags:
+ - {key: db.type, value: MongoDB}
+ - {key: db.bind_vars, value: not null}
+ skipAnalysis: 'false'
+ - operationName: MongoDB/FindOperation
+ operationId: 0
+ parentSpanId: 0
+ spanId: 3
+ spanLayer: Database
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 42
+ isError: false
+ spanType: Exit
+ peer: mongodb-server:27017
+ tags:
+ - {key: db.type, value: MongoDB}
+ - {key: db.bind_vars, value: '{"name": "org"}'}
+ skipAnalysis: 'false'
+ - operationName: MongoDB/MixedBulkWriteOperation
+ operationId: 0
+ parentSpanId: 0
+ spanId: 4
+ spanLayer: Database
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 42
+ isError: false
+ spanType: Exit
+ peer: mongodb-server:27017
+ tags:
+ - {key: db.type, value: MongoDB}
+ - {key: db.bind_vars, value: '{"name": "org"},'}
+ skipAnalysis: 'false'
+ - operationName: MongoDB/FindOperation
+ operationId: 0
+ parentSpanId: 0
+ spanId: 5
+ spanLayer: Database
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 42
+ isError: false
+ spanType: Exit
+ peer: mongodb-server:27017
+ tags:
+ - {key: db.type, value: MongoDB}
+ - {key: db.bind_vars, value: '{"name": "testA"}'}
+ skipAnalysis: 'false'
+ - operationName: MongoDB/MixedBulkWriteOperation
+ operationId: 0
+ parentSpanId: 0
+ spanId: 6
+ spanLayer: Database
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 42
+ isError: false
+ spanType: Exit
+ peer: mongodb-server:27017
+ tags:
+ - {key: db.type, value: MongoDB}
+ - {key: db.bind_vars, value: '{"id": "1"},'}
+ skipAnalysis: 'false'
+ - operationName: MongoDB/DropDatabaseOperation
+ operationId: 0
+ parentSpanId: 0
+ spanId: 7
+ spanLayer: Database
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 42
+ isError: false
+ spanType: Exit
+ peer: mongodb-server:27017
+ tags:
+ - {key: db.type, value: MongoDB}
+ - {key: db.bind_vars, value: null}
+ skipAnalysis: 'false'
+ - operationName: /mongodb-4.x-scenario/case/mongodb-4.x-scenario
+ operationId: 0
+ parentSpanId: -1
+ spanId: 0
+ spanLayer: Http
+ startTime: nq 0
+ endTime: nq 0
+ componentId: 1
+ isError: false
+ spanType: Entry
+ peer: ''
+ tags:
+ - {key: url, value: 'http://localhost:8080/mongodb-4.x-scenario/case/mongodb-4.x-scenario'}
+ - {key: http.method, value: GET}
+ skipAnalysis: 'false'
diff --git a/test/plugin/scenarios/mongodb-4.x-scenario/configuration.yml b/test/plugin/scenarios/mongodb-4.x-scenario/configuration.yml
new file mode 100644
index 0000000000..47e10eaaab
--- /dev/null
+++ b/test/plugin/scenarios/mongodb-4.x-scenario/configuration.yml
@@ -0,0 +1,24 @@
+# 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/mongodb-4.x-scenario/case/mongodb-4.x-scenario
+healthCheck: http://localhost:8080/mongodb-4.x-scenario/case/healthCheck
+startScript: ./bin/startup.sh
+dependencies:
+ mongodb-server:
+ image: mongo:4.2
+ hostname: mongodb-server
diff --git a/test/plugin/scenarios/mongodb-4.x-scenario/pom.xml b/test/plugin/scenarios/mongodb-4.x-scenario/pom.xml
new file mode 100644
index 0000000000..2552518f84
--- /dev/null
+++ b/test/plugin/scenarios/mongodb-4.x-scenario/pom.xml
@@ -0,0 +1,145 @@
+
+
+
+
+ org.apache.skywalking.apm.testcase
+ mongodb-4.x-scenario
+ 1.0.0
+ jar
+
+ 4.0.0
+
+
+ UTF-8
+ 1.8
+ 4.0.0
+ 2.1.6.RELEASE
+ 1.18.10
+
+
+ skywalking-mongodb-4.x-scenario
+
+
+
+
+ org.springframework.boot
+ spring-boot-dependencies
+ ${spring-boot-version}
+ pom
+ import
+
+
+ org.mongodb
+ mongodb-driver-core
+ ${test.framework.version}
+
+
+ org.mongodb
+ mongodb-driver-sync
+ ${test.framework.version}
+
+
+ org.mongodb
+ bson
+ ${test.framework.version}
+
+
+
+
+
+
+ org.mongodb
+ mongodb-driver-core
+
+
+ org.mongodb
+ mongodb-driver-sync
+
+
+ org.mongodb
+ bson
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-log4j2
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+ provided
+
+
+
+
+ mongodb-4.x-scenario
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ repackage
+
+
+
+
+
+ maven-compiler-plugin
+
+ ${compiler.version}
+ ${compiler.version}
+ ${project.build.sourceEncoding}
+
+
+
+ org.apache.maven.plugins
+ maven-assembly-plugin
+
+
+ assemble
+ package
+
+ single
+
+
+
+ src/main/assembly/assembly.xml
+
+ ./target/
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/plugin/scenarios/mongodb-4.x-scenario/src/main/assembly/assembly.xml b/test/plugin/scenarios/mongodb-4.x-scenario/src/main/assembly/assembly.xml
new file mode 100644
index 0000000000..01b0eb0118
--- /dev/null
+++ b/test/plugin/scenarios/mongodb-4.x-scenario/src/main/assembly/assembly.xml
@@ -0,0 +1,41 @@
+
+
+
+
+ zip
+
+
+
+
+ ./bin
+ 0775
+
+
+
+
+
+ ${project.build.directory}/mongodb-4.x-scenario.jar
+ ./libs
+ 0775
+
+
+
diff --git a/test/plugin/scenarios/mongodb-4.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/mongodb/Application.java b/test/plugin/scenarios/mongodb-4.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/mongodb/Application.java
new file mode 100644
index 0000000000..1dd56fec23
--- /dev/null
+++ b/test/plugin/scenarios/mongodb-4.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/mongodb/Application.java
@@ -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.
+ *
+ */
+
+package org.apache.skywalking.apm.testcase.mongodb;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class Application {
+
+ public static void main(String[] args) {
+ try {
+ SpringApplication.run(Application.class, args);
+ } catch (Exception e) {
+ // Never do this
+ }
+ }
+}
diff --git a/test/plugin/scenarios/mongodb-4.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/mongodb/controller/CaseController.java b/test/plugin/scenarios/mongodb-4.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/mongodb/controller/CaseController.java
new file mode 100644
index 0000000000..298ac22916
--- /dev/null
+++ b/test/plugin/scenarios/mongodb-4.x-scenario/src/main/java/org/apache/skywalking/apm/testcase/mongodb/controller/CaseController.java
@@ -0,0 +1,77 @@
+/*
+ * 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.mongodb.controller;
+
+import com.mongodb.client.*;
+import org.bson.BsonDocument;
+import org.bson.Document;
+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 static com.mongodb.client.model.Filters.eq;
+
+@RestController
+@RequestMapping("/case")
+public class CaseController {
+
+ @Value(value = "${mongodb.uri}")
+ private String connectionString;
+
+ @GetMapping("/healthCheck")
+ public String health() {
+ // check connect to mongodb server
+ try (MongoClient mongoClient = MongoClients.create(connectionString)) {
+ return "success";
+ }
+ }
+
+ @RequestMapping("/mongodb-4.x-scenario")
+ public String mongoDBCase() {
+ try (MongoClient mongoClient = MongoClients.create(connectionString)) {
+ MongoDatabase db = mongoClient.getDatabase("test-database");
+ // CreateCollectionOperation
+ db.createCollection("testCollection");
+
+ MongoCollection collection = db.getCollection("testCollection");
+ Document document = Document.parse("{id: 1, name: \"test\"}");
+ // MixedBulkWriteOperation
+ collection.insertOne(document);
+
+ // FindOperation
+ FindIterable findIterable = collection.find(eq("name", "org"));
+ findIterable.first();
+
+ // MixedBulkWriteOperation
+ collection.updateOne(eq("name", "org"), BsonDocument.parse("{ $set : { \"name\": \"testA\"} }"));
+
+ // FindOperation
+ findIterable = collection.find(eq("name", "testA"));
+ findIterable.first();
+
+ // MixedBulkWriteOperation
+ collection.deleteOne(eq("id", "1"));
+
+ // DropDatabaseOperation
+ mongoClient.getDatabase("test-database").drop();
+ }
+ return "success";
+ }
+}
diff --git a/test/plugin/scenarios/mongodb-4.x-scenario/src/main/resources/application.yaml b/test/plugin/scenarios/mongodb-4.x-scenario/src/main/resources/application.yaml
new file mode 100644
index 0000000000..200fb5d3de
--- /dev/null
+++ b/test/plugin/scenarios/mongodb-4.x-scenario/src/main/resources/application.yaml
@@ -0,0 +1,25 @@
+#
+# 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.
+#
+#
+server:
+ port: 8080
+ servlet:
+ context-path: /mongodb-4.x-scenario
+logging:
+ config: classpath:log4j2.xml
+mongodb:
+ uri: mongodb://mongodb-server/test
\ No newline at end of file
diff --git a/test/plugin/scenarios/mongodb-4.x-scenario/src/main/resources/log4j2.xml b/test/plugin/scenarios/mongodb-4.x-scenario/src/main/resources/log4j2.xml
new file mode 100644
index 0000000000..9849ed5a8a
--- /dev/null
+++ b/test/plugin/scenarios/mongodb-4.x-scenario/src/main/resources/log4j2.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/test/plugin/scenarios/mongodb-4.x-scenario/support-version.list b/test/plugin/scenarios/mongodb-4.x-scenario/support-version.list
new file mode 100644
index 0000000000..53a8936935
--- /dev/null
+++ b/test/plugin/scenarios/mongodb-4.x-scenario/support-version.list
@@ -0,0 +1,23 @@
+# 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.
+
+4.0.0
+4.0.1
+4.0.2
+4.0.3
+4.0.4
+4.0.5
+4.1.0
\ No newline at end of file