From 80af1667528cfd8924cce5ca4bc62a31c116974f Mon Sep 17 00:00:00 2001 From: scolia Date: Thu, 24 Oct 2019 11:16:56 -0500 Subject: [PATCH] feat: add support of mongodb v3.7.x or higher (#3591) * feat: add support of mongodb v3.7.x or higher * feat: remove mapping, using EnhancedInstance to mark remotePeer * feat: add support of v3.6.x * fix: disable support will 3.6.x, which will throw ClassCircularityError exception * style: clean code * fix support of 3.8.x~3.11.1, update test case and doc * update stage name * rename class * rename stage --- Jenkinsfile-Agent-Test-3 | 4 +- .../apm-sdk-plugin/mongodb-3.x-plugin/pom.xml | 2 +- .../mongodb/v3/MongoDBMethodInterceptor.java | 211 ------------------ .../{ => v30}/MongoDBInstrumentation.java | 87 +++++--- .../MongoDBClientDelegateInstrumentation.java | 100 +++++++++ ...ngoDBOperationExecutorInstrumentation.java | 96 ++++++++ ...ngoDBOperationExecutorInstrumentation.java | 91 ++++++++ .../interceptor/v30/MongoDBInterceptor.java | 83 +++++++ .../v37/MongoDBClientDelegateInterceptor.java | 75 +++++++ .../MongoDBOperationExecutorInterceptor.java | 70 ++++++ .../mongodb/v3/support/MongoConstants.java | 38 ++++ .../v3/support/MongoOperationHelper.java | 131 +++++++++++ .../v3/support/MongoRemotePeerHelper.java | 44 ++++ .../mongodb/v3/support/MongoSpanHelper.java | 48 ++++ .../src/main/resources/skywalking-plugin.def | 8 +- .../v30/MongoDBInterceptorTest.java} | 25 +-- .../MongoDBClientDelegateInterceptorTest.java | 76 +++++++ ...ngoDBOperationExecutorInterceptorTest.java | 139 ++++++++++++ .../java-agent/Supported-list.md | 2 +- .../mongodb-3.x-scenario/support-version.list | 12 + 20 files changed, 1078 insertions(+), 264 deletions(-) delete mode 100644 apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/MongoDBMethodInterceptor.java rename apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/{ => v30}/MongoDBInstrumentation.java (54%) create mode 100644 apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/v37/MongoDBClientDelegateInstrumentation.java create mode 100644 apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/v37/MongoDBOperationExecutorInstrumentation.java create mode 100644 apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/v38/MongoDBOperationExecutorInstrumentation.java create mode 100644 apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/v30/MongoDBInterceptor.java create mode 100644 apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/v37/MongoDBClientDelegateInterceptor.java create mode 100644 apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/v37/MongoDBOperationExecutorInterceptor.java create mode 100644 apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/support/MongoConstants.java create mode 100644 apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/support/MongoOperationHelper.java create mode 100644 apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/support/MongoRemotePeerHelper.java create mode 100644 apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/support/MongoSpanHelper.java rename apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/mongodb/v3/{MongoDBMethodInterceptorTest.java => interceptor/v30/MongoDBInterceptorTest.java} (87%) create mode 100644 apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/v37/MongoDBClientDelegateInterceptorTest.java create mode 100644 apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/v37/MongoDBOperationExecutorInterceptorTest.java diff --git a/Jenkinsfile-Agent-Test-3 b/Jenkinsfile-Agent-Test-3 index 6469a9dd68..db8467983f 100755 --- a/Jenkinsfile-Agent-Test-3 +++ b/Jenkinsfile-Agent-Test-3 @@ -66,7 +66,7 @@ pipeline { sh './mvnw -f test/plugin/pom.xml clean package -DskipTests -Dbuild_id=wl3_${BUILD_ID} docker:build' } } - stage('Test Cases Report (137)') { + stage('Test Cases Report (149)') { steps { echo "reserve." } @@ -96,7 +96,7 @@ pipeline { sh 'bash test/plugin/run.sh --build_id=wl3_${BUILD_ID} sofarpc-scenario' } } - stage('mongodb 3.0-3.5.0 (5)') { + stage('mongodb 3.4.0-3.11.1 (17)') { steps { sh 'bash test/plugin/run.sh --build_id=wl3_${BUILD_ID} mongodb-3.x-scenario' } diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/pom.xml index e4dd6f8aa1..6ce09dbac7 100644 --- a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/pom.xml @@ -32,7 +32,7 @@ UTF-8 - 3.4.2 + 3.11.0 diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/MongoDBMethodInterceptor.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/MongoDBMethodInterceptor.java deleted file mode 100644 index 5f85d747a1..0000000000 --- a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/MongoDBMethodInterceptor.java +++ /dev/null @@ -1,211 +0,0 @@ -/* - * 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.v3; - -import com.mongodb.ReadPreference; -import com.mongodb.ServerAddress; -import com.mongodb.bulk.DeleteRequest; -import com.mongodb.bulk.InsertRequest; -import com.mongodb.bulk.UpdateRequest; -import com.mongodb.bulk.WriteRequest; -import com.mongodb.connection.Cluster; -import com.mongodb.connection.ServerDescription; -import com.mongodb.operation.CountOperation; -import com.mongodb.operation.CreateCollectionOperation; -import com.mongodb.operation.CreateIndexesOperation; -import com.mongodb.operation.CreateViewOperation; -import com.mongodb.operation.DeleteOperation; -import com.mongodb.operation.DistinctOperation; -import com.mongodb.operation.FindAndDeleteOperation; -import com.mongodb.operation.FindAndReplaceOperation; -import com.mongodb.operation.FindAndUpdateOperation; -import com.mongodb.operation.FindOperation; -import com.mongodb.operation.GroupOperation; -import com.mongodb.operation.InsertOperation; -import com.mongodb.operation.ListCollectionsOperation; -import com.mongodb.operation.MapReduceToCollectionOperation; -import com.mongodb.operation.MapReduceWithInlineResultsOperation; -import com.mongodb.operation.MixedBulkWriteOperation; -import com.mongodb.operation.ReadOperation; -import com.mongodb.operation.UpdateOperation; -import com.mongodb.operation.WriteOperation; -import java.lang.reflect.Method; -import java.util.List; -import org.apache.skywalking.apm.agent.core.conf.Config; -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.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.network.trace.component.ComponentsDefine; -import org.bson.BsonDocument; - -/** - * {@link MongoDBMethodInterceptor} intercept method of {@link com.mongodb.Mongo#execute(ReadOperation, ReadPreference)} - * or {@link com.mongodb.Mongo#execute(WriteOperation)}. record the mongoDB host, operation name and the key of the - * operation. - * - * @author baiyang - */ -public class MongoDBMethodInterceptor implements InstanceMethodsAroundInterceptor, InstanceConstructorInterceptor { - - private static final String DB_TYPE = "MongoDB"; - - private static final String MONGO_DB_OP_PREFIX = "MongoDB/"; - - private static final int FILTER_LENGTH_LIMIT = 256; - - private static final String EMPTY = ""; - - /** - * Convert ReadOperation interface or WriteOperation interface to the implementation class. Get the method name and - * filter info. - */ - @SuppressWarnings("rawtypes") - private 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 GroupOperation) { - BsonDocument filter = ((GroupOperation)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 writeRequestList = ((MixedBulkWriteOperation)obj).getWriteRequests(); - return getFilter(writeRequestList); - } else { - return EMPTY; - } - } - - private String getFilter(List 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(","); - } - if (params.length() > FILTER_LENGTH_LIMIT) { - params.append("..."); - break; - } - } - return params.toString(); - } - - private String limitFilter(String filter) { - final StringBuilder params = new StringBuilder(); - if (filter.length() > FILTER_LENGTH_LIMIT) { - return params.append(filter.substring(0, FILTER_LENGTH_LIMIT)).append("...").toString(); - } else { - return filter; - } - } - - @Override public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, - Class[] argumentsTypes, MethodInterceptResult result) throws Throwable { - Object[] arguments = allArguments; - - String executeMethod = arguments[0].getClass().getSimpleName(); - String remotePeer = (String)objInst.getSkyWalkingDynamicField(); - AbstractSpan span = ContextManager.createExitSpan(MONGO_DB_OP_PREFIX + executeMethod, new ContextCarrier(), remotePeer); - span.setComponent(ComponentsDefine.MONGO_DRIVER); - Tags.DB_TYPE.set(span, DB_TYPE); - SpanLayer.asDB(span); - - if (Config.Plugin.MongoDB.TRACE_PARAM) { - Tags.DB_STATEMENT.set(span, executeMethod + " " + this.getTraceParam(arguments[0])); - } - - } - - @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) { - AbstractSpan activeSpan = ContextManager.activeSpan(); - activeSpan.errorOccurred(); - activeSpan.log(t); - } - - @Override - public void onConstruct(EnhancedInstance objInst, Object[] allArguments) { - Cluster cluster = (Cluster)allArguments[0]; - StringBuilder peers = new StringBuilder(); - for (ServerDescription description : cluster.getDescription().getServerDescriptions()) { - ServerAddress address = description.getAddress(); - peers.append(address.getHost() + ":" + address.getPort() + ";"); - } - - objInst.setSkyWalkingDynamicField(peers.subSequence(0, peers.length() - 1).toString()); - } -} diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/MongoDBInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/v30/MongoDBInstrumentation.java similarity index 54% rename from apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/MongoDBInstrumentation.java rename to apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/v30/MongoDBInstrumentation.java index 40049e6954..7b0ac23951 100644 --- a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/MongoDBInstrumentation.java +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/v30/MongoDBInstrumentation.java @@ -17,67 +17,86 @@ */ -package org.apache.skywalking.apm.plugin.mongodb.v3.define; +package org.apache.skywalking.apm.plugin.mongodb.v3.define.v30; 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.agent.core.plugin.match.NameMatch; -import org.apache.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint; +import org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.v30.MongoDBInterceptor; import static net.bytebuddy.matcher.ElementMatchers.named; import static org.apache.skywalking.apm.agent.core.plugin.bytebuddy.ArgumentTypeNameMatch.takesArgumentWithType; +/** + * Enhance {@code com.mongodb.Mongo} instance, and intercept {@code com.mongodb.Mongo#execute(...)} method, + * this method is a unified entrance of execute mongo command. + *

+ * support: 3.0.x~3.5.x + * + * @author scolia + * @see MongoDBInterceptor + */ +@SuppressWarnings({"Duplicates"}) public class MongoDBInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + private static final String WITNESS_CLASS = "com.mongodb.connection.WriteCommandProtocol"; + private static final String ENHANCE_CLASS = "com.mongodb.Mongo"; - private static final String MONGDB_METHOD_INTERCET_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v3.MongoDBMethodInterceptor"; + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.v30.MongoDBInterceptor"; + + @Override + protected String[] witnessClasses() { + // this class only exist in version: 3.0.x~3.5.x + return new String[]{WITNESS_CLASS}; + } + + @Override + protected ClassMatch enhanceClass() { + return NameMatch.byName(ENHANCE_CLASS); + } + @Override public ConstructorInterceptPoint[] getConstructorsInterceptPoints() { - return new ConstructorInterceptPoint[] { - new ConstructorInterceptPoint() { - @Override - public ElementMatcher getConstructorMatcher() { - return takesArgumentWithType(0, "com.mongodb.connection.Cluster"); - } - - @Override - public String getConstructorInterceptor() { - return MONGDB_METHOD_INTERCET_CLASS; - } + return new ConstructorInterceptPoint[]{new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return takesArgumentWithType(0, "com.mongodb.connection.Cluster"); + } + + @Override + public String getConstructorInterceptor() { + return INTERCEPTOR_CLASS; } + } }; } @Override public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { - return new InstanceMethodsInterceptPoint[] { - new InstanceMethodsInterceptPoint() { - @Override - public ElementMatcher getMethodsMatcher() { - return named("execute"); - } - - @Override - public String getMethodsInterceptor() { - return MONGDB_METHOD_INTERCET_CLASS; - } - - @Override - public boolean isOverrideArgs() { - return false; - } + return new InstanceMethodsInterceptPoint[]{new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named("execute"); + } + + @Override + public String getMethodsInterceptor() { + return INTERCEPTOR_CLASS; } + + @Override + public boolean isOverrideArgs() { + return false; + } + } }; } - @Override - protected ClassMatch enhanceClass() { - return NameMatch.byName(ENHANCE_CLASS); - } } diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/v37/MongoDBClientDelegateInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/v37/MongoDBClientDelegateInstrumentation.java new file mode 100644 index 0000000000..df74c59ba6 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/v37/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.v3.define.v37; + +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.v3.interceptor.v37.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. we can mark OperationExecutor + * which connection belongs to. + *

+ * support: 3.7.x or higher + * + * @author scolia + * @see MongoDBOperationExecutorInstrumentation + * @see MongoDBClientDelegateInterceptor + */ +public class MongoDBClientDelegateInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "com.mongodb.client.internal.MongoClientDelegate"; + + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.v37.MongoDBClientDelegateInterceptor"; + + @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, "com.mongodb.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-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/v37/MongoDBOperationExecutorInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/v37/MongoDBOperationExecutorInstrumentation.java new file mode 100644 index 0000000000..4eaadd3154 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/v37/MongoDBOperationExecutorInstrumentation.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.plugin.mongodb.v3.define.v37; + +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; +import org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.v37.MongoDBOperationExecutorInterceptor; + +/** + * {@code com.mongodb.client.internal.OperationExecutor} which is unified entrance of execute mongo command. + * so we can intercept {@code com.mongodb.client.internal.OperationExecutor#execute(...)} method + * to known which command will be execute. + *

+ * support: 3.7.x + * + * @author scolia + * @see MongoDBOperationExecutorInterceptor + */ +@SuppressWarnings({"Duplicates"}) +public class MongoDBOperationExecutorInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String WITNESS_CLASS = "com.mongodb.client.internal.MongoClientDelegate"; + + private static final String ENHANCE_CLASS = "com.mongodb.client.internal.MongoClientDelegate$DelegateOperationExecutor"; + + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.v37.MongoDBOperationExecutorInterceptor"; + + private static final String METHOD_NAME = "execute"; + + private static final String ARGUMENT_TYPE = "com.mongodb.session.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 + // 3.7.x + .named(METHOD_NAME).and(ArgumentTypeNameMatch.takesArgumentWithType(1, ARGUMENT_TYPE)) + .or(ElementMatchers.named(METHOD_NAME) + .and(ArgumentTypeNameMatch.takesArgumentWithType(2, ARGUMENT_TYPE)) + ); + } + + @Override + public String getMethodsInterceptor() { + return INTERCEPTOR_CLASS; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/v38/MongoDBOperationExecutorInstrumentation.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/v38/MongoDBOperationExecutorInstrumentation.java new file mode 100644 index 0000000000..20fa9033e6 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/define/v38/MongoDBOperationExecutorInstrumentation.java @@ -0,0 +1,91 @@ +/* + * 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.v3.define.v38; + +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 whit {@link org.apache.skywalking.apm.plugin.mongodb.v3.define.v37.MongoDBOperationExecutorInstrumentation} + *

+ * support: 3.8.x or higher + * + * @author scolia + */ +public class MongoDBOperationExecutorInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String WITNESS_CLASS = "com.mongodb.client.ClientSession"; + + private static final String ENHANCE_CLASS = "com.mongodb.client.internal.MongoClientDelegate$DelegateOperationExecutor"; + + private static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.v37.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 + // 3.8.x~3.11.x + .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-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/v30/MongoDBInterceptor.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/v30/MongoDBInterceptor.java new file mode 100644 index 0000000000..cca7b99221 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/v30/MongoDBInterceptor.java @@ -0,0 +1,83 @@ +/* + * 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.v3.interceptor.v30; + +import com.mongodb.connection.Cluster; +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.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.v3.support.MongoRemotePeerHelper; +import org.apache.skywalking.apm.plugin.mongodb.v3.support.MongoSpanHelper; + +import java.lang.reflect.Method; + +/** + * Intercept method of {@code com.mongodb.Mongo#execute(ReadOperation, ReadPreference)} or + * {@code com.mongodb.Mongo#execute(WriteOperation)}. record the MongoDB host, operation name and the key of the + * operation. + *

+ * only supported: 3.0.x-3.5.x + * + * @author scolia + */ +@SuppressWarnings({"deprecation", "Duplicates"}) +public class MongoDBInterceptor implements InstanceMethodsAroundInterceptor, InstanceConstructorInterceptor { + + private static final ILog logger = LogManager.getLogger(MongoDBInterceptor.class); + + @Override + public void onConstruct(EnhancedInstance objInst, Object[] allArguments) { + Cluster cluster = (Cluster) allArguments[0]; + String peers = MongoRemotePeerHelper.getRemotePeer(cluster); + objInst.setSkyWalkingDynamicField(peers); + } + + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, MethodInterceptResult result) { + String executeMethod = allArguments[0].getClass().getSimpleName(); + 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.errorOccurred(); + activeSpan.log(t); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/v37/MongoDBClientDelegateInterceptor.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/v37/MongoDBClientDelegateInterceptor.java new file mode 100644 index 0000000000..aba5a887b5 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/v37/MongoDBClientDelegateInterceptor.java @@ -0,0 +1,75 @@ +/* + * 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.v3.interceptor.v37; + +import com.mongodb.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.v3.support.MongoRemotePeerHelper; + +import java.lang.reflect.Method; + +/** + * @author scolia + */ +@SuppressWarnings("Duplicates") +public class MongoDBClientDelegateInterceptor implements InstanceConstructorInterceptor, InstanceMethodsAroundInterceptor { + + private static final ILog logger = LogManager.getLogger(MongoDBClientDelegateInterceptor.class); + + @SuppressWarnings("deprecation") + @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 will be wrapper 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-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/v37/MongoDBOperationExecutorInterceptor.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/v37/MongoDBOperationExecutorInterceptor.java new file mode 100644 index 0000000000..3eeee41283 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/v37/MongoDBOperationExecutorInterceptor.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.v3.interceptor.v37; + +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.v3.support.MongoSpanHelper; + +import java.lang.reflect.Method; + +/** + * @author scolia + */ +@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 be mark it's 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.errorOccurred(); + activeSpan.log(t); + } + + +} diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/support/MongoConstants.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/support/MongoConstants.java new file mode 100644 index 0000000000..16190572d9 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/support/MongoConstants.java @@ -0,0 +1,38 @@ +/* + * 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.v3.support; + +/** + * @author scolia + */ +public class MongoConstants { + + private MongoConstants() { + } + + public static final String DB_TYPE = "MongoDB"; + + public static final String MONGO_DB_OP_PREFIX = "MongoDB/"; + + public static final int FILTER_LENGTH_LIMIT = 256; + + public static final String EMPTY = ""; + +} diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/support/MongoOperationHelper.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/support/MongoOperationHelper.java new file mode 100644 index 0000000000..c038cd5b9c --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/support/MongoOperationHelper.java @@ -0,0 +1,131 @@ +/* + * 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.v3.support; + +import com.mongodb.bulk.DeleteRequest; +import com.mongodb.bulk.InsertRequest; +import com.mongodb.bulk.UpdateRequest; +import com.mongodb.bulk.WriteRequest; +import com.mongodb.operation.*; +import org.bson.BsonDocument; + +import java.util.List; + +/** + * @author scolia + */ +@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. + */ + @SuppressWarnings("rawtypes") + 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 GroupOperation) { + BsonDocument filter = ((GroupOperation) 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 writeRequestList = ((MixedBulkWriteOperation) obj).getWriteRequests(); + return getFilter(writeRequestList); + } else { + return MongoConstants.EMPTY; + } + } + + private static String getFilter(List 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(","); + } + if (params.length() > MongoConstants.FILTER_LENGTH_LIMIT) { + params.append("..."); + break; + } + } + return params.toString(); + } + + private static String limitFilter(String filter) { + final StringBuilder params = new StringBuilder(); + if (filter.length() > MongoConstants.FILTER_LENGTH_LIMIT) { + return params.append(filter, 0, MongoConstants.FILTER_LENGTH_LIMIT).append("...").toString(); + } else { + return filter; + } + } + +} diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/support/MongoRemotePeerHelper.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/support/MongoRemotePeerHelper.java new file mode 100644 index 0000000000..8f73d4e450 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/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.v3.support; + +import com.mongodb.ServerAddress; +import com.mongodb.connection.Cluster; +import com.mongodb.connection.ServerDescription; + +/** + * @author scolia + */ +@SuppressWarnings("deprecation") +public class MongoRemotePeerHelper { + + private MongoRemotePeerHelper() { + } + + + public static String getRemotePeer(Cluster cluster) { + StringBuilder peersBuilder = new StringBuilder(); + for (ServerDescription description : cluster.getDescription().getAll()) { + 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-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/support/MongoSpanHelper.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/support/MongoSpanHelper.java new file mode 100644 index 0000000000..c820179a75 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/java/org/apache/skywalking/apm/plugin/mongodb/v3/support/MongoSpanHelper.java @@ -0,0 +1,48 @@ +/* + * 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.v3.support; + +import org.apache.skywalking.apm.agent.core.conf.Config; +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; + +/** + * @author scolia + */ +public class MongoSpanHelper { + + private MongoSpanHelper() { + } + + 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 (Config.Plugin.MongoDB.TRACE_PARAM) { + Tags.DB_STATEMENT.set(span, executeMethod + " " + MongoOperationHelper.getTraceParam(operation)); + } + } +} diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/resources/skywalking-plugin.def index 3985450977..77c4d8aa68 100644 --- a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/resources/skywalking-plugin.def +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/main/resources/skywalking-plugin.def @@ -14,4 +14,10 @@ # See the License for the specific language governing permissions and # limitations under the License. -mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.MongoDBInstrumentation +# v3.0.x~v3.5.x +mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.v30.MongoDBInstrumentation +# v3.7.x~ +mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.v37.MongoDBClientDelegateInstrumentation +mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.v37.MongoDBOperationExecutorInstrumentation +# v3.8.x~ +mongodb-3.x=org.apache.skywalking.apm.plugin.mongodb.v3.define.v38.MongoDBOperationExecutorInstrumentation \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/mongodb/v3/MongoDBMethodInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/v30/MongoDBInterceptorTest.java similarity index 87% rename from apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/mongodb/v3/MongoDBMethodInterceptorTest.java rename to apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/v30/MongoDBInterceptorTest.java index 057631c214..2e4ee22157 100644 --- a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/mongodb/v3/MongoDBMethodInterceptorTest.java +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/v30/MongoDBInterceptorTest.java @@ -17,13 +17,11 @@ */ -package org.apache.skywalking.apm.plugin.mongodb.v3; +package org.apache.skywalking.apm.plugin.mongodb.v3.interceptor.v30; import com.mongodb.Mongo; import com.mongodb.MongoNamespace; import com.mongodb.operation.FindOperation; -import java.lang.reflect.Method; -import java.util.List; import org.apache.skywalking.apm.agent.core.conf.Config; import org.apache.skywalking.apm.agent.core.context.trace.AbstractTracingSpan; import org.apache.skywalking.apm.agent.core.context.trace.LogDataEntity; @@ -33,11 +31,7 @@ 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.agent.test.tools.*; import org.bson.BsonDocument; import org.bson.BsonString; import org.bson.codecs.Decoder; @@ -52,13 +46,16 @@ 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 MongoDBMethodInterceptorTest { +public class MongoDBInterceptorTest { @SegmentStoragePoint private SegmentStorage segmentStorage; @@ -66,7 +63,7 @@ public class MongoDBMethodInterceptorTest { @Rule public AgentServiceRule serviceRule = new AgentServiceRule(); - private MongoDBMethodInterceptor interceptor; + private MongoDBInterceptor interceptor; @Mock private EnhancedInstance enhancedInstance; @@ -78,7 +75,7 @@ public class MongoDBMethodInterceptorTest { @Before public void setUp() throws Exception { - interceptor = new MongoDBMethodInterceptor(); + interceptor = new MongoDBInterceptor(); Config.Plugin.MongoDB.TRACE_PARAM = true; @@ -91,8 +88,8 @@ public class MongoDBMethodInterceptorTest { FindOperation findOperation = new FindOperation(mongoNamespace, decoder); findOperation.filter(document); - arguments = new Object[] {findOperation}; - argumentTypes = new Class[] {findOperation.getClass()}; + arguments = new Object[]{findOperation}; + argumentTypes = new Class[]{findOperation.getClass()}; } @Test @@ -125,7 +122,7 @@ public class MongoDBMethodInterceptorTest { assertThat(span.getOperationName(), is("MongoDB/FindOperation")); assertThat(SpanHelper.getComponentId(span), is(42)); List tags = SpanHelper.getTags(span); - assertThat(tags.get(1).getValue(), is("FindOperation { \"name\" : \"by\" }")); + assertThat(tags.get(1).getValue(), is("FindOperation {\"name\": \"by\"}")); assertThat(tags.get(0).getValue(), is("MongoDB")); assertThat(span.isExit(), is(true)); assertThat(SpanHelper.getLayer(span), CoreMatchers.is(SpanLayer.DB)); diff --git a/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/v37/MongoDBClientDelegateInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/v37/MongoDBClientDelegateInterceptorTest.java new file mode 100644 index 0000000000..1190e2dfe8 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/v37/MongoDBClientDelegateInterceptorTest.java @@ -0,0 +1,76 @@ +/* + * 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.v3.interceptor.v37; + +import org.apache.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +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; + +/** + * @author scolia + */ +@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-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/v37/MongoDBOperationExecutorInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/v37/MongoDBOperationExecutorInterceptorTest.java new file mode 100644 index 0000000000..6ccd15c50e --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/mongodb-3.x-plugin/src/test/java/org/apache/skywalking/apm/plugin/mongodb/v3/interceptor/v37/MongoDBOperationExecutorInterceptorTest.java @@ -0,0 +1,139 @@ +/* + * 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.v3.interceptor.v37; + +import com.mongodb.MongoNamespace; +import com.mongodb.ReadConcern; +import com.mongodb.client.internal.OperationExecutor; +import com.mongodb.operation.FindOperation; +import com.mongodb.operation.WriteOperation; +import org.apache.skywalking.apm.agent.core.conf.Config; +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.*; +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; + +/** + * @author scolia + */ +@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(); + + Config.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); + assertRedisSpan(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); + assertRedisSpan(spans.get(0)); + List logDataEntities = SpanHelper.getLogs(spans.get(0)); + assertThat(logDataEntities.size(), is(1)); + SpanAssert.assertException(logDataEntities.get(0), RuntimeException.class); + } + + private void assertRedisSpan(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("FindOperation {\"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/docs/en/setup/service-agent/java-agent/Supported-list.md b/docs/en/setup/service-agent/java-agent/Supported-list.md index 190452ea4e..cc139fda3d 100644 --- a/docs/en/setup/service-agent/java-agent/Supported-list.md +++ b/docs/en/setup/service-agent/java-agent/Supported-list.md @@ -51,7 +51,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 (Optional²) - * [MongoDB Java Driver](https://github.com/mongodb/mongo-java-driver) 2.13-2.14,3.4.0-3.5.0 + * [MongoDB Java Driver](https://github.com/mongodb/mongo-java-driver) 2.13-2.14, 3.4.0-3.5.0, 3.7.x-3.11.1 * 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-3.x-scenario/support-version.list b/test/plugin/scenarios/mongodb-3.x-scenario/support-version.list index 09d38b5345..42b54a65e0 100644 --- a/test/plugin/scenarios/mongodb-3.x-scenario/support-version.list +++ b/test/plugin/scenarios/mongodb-3.x-scenario/support-version.list @@ -19,3 +19,15 @@ 3.4.2 3.4.3 3.5.0 +3.7.0 +3.7.1 +3.8.0 +3.8.1 +3.8.2 +3.9.0 +3.9.1 +3.10.0 +3.10.1 +3.10.2 +3.11.0 +3.11.1 \ No newline at end of file -- GitLab