diff --git a/README.md b/README.md
index e6bcd0756..df784b161 100644
--- a/README.md
+++ b/README.md
@@ -40,7 +40,7 @@ _In chronological order_
* 徐妍 [@TastySummer](https://github.com/TastySummer)
* 彭勇升 [@pengys5](https://github.com/pengys5)
* 戴文
-
+* 柏杨 [@bai-yang](https://github.com/bai-yang)
# Screenshots
- Topological graph of application clusters.
diff --git a/skywalking-sniffer/skywalking-agent/pom.xml b/skywalking-sniffer/skywalking-agent/pom.xml
index 5ff241eae..16a3c2ddb 100644
--- a/skywalking-sniffer/skywalking-agent/pom.xml
+++ b/skywalking-sniffer/skywalking-agent/pom.xml
@@ -55,6 +55,11 @@
motan-plugin
${project.version}
+
+ com.a.eye
+ skywalking-mongodb-3.x-plugin
+ ${project.version}
+
diff --git a/skywalking-sniffer/skywalking-api/src/main/java/com/a/eye/skywalking/api/conf/Config.java b/skywalking-sniffer/skywalking-api/src/main/java/com/a/eye/skywalking/api/conf/Config.java
index 96aaafa13..1c88af5aa 100644
--- a/skywalking-sniffer/skywalking-api/src/main/java/com/a/eye/skywalking/api/conf/Config.java
+++ b/skywalking-sniffer/skywalking-api/src/main/java/com/a/eye/skywalking/api/conf/Config.java
@@ -81,4 +81,14 @@ public class Config {
*/
public static LogLevel LEVEL = LogLevel.DEBUG;
}
+
+ public static class Plugin {
+ public static class MongoDB {
+ /**
+ * If true, trace all the parameters, default is false.
+ * Only trace the operation, not include parameters.
+ */
+ public static boolean TRACE_PARAM = false;
+ }
+ }
}
diff --git a/skywalking-sniffer/skywalking-sdk-plugin/mongodb-3.x-plugin/pom.xml b/skywalking-sniffer/skywalking-sdk-plugin/mongodb-3.x-plugin/pom.xml
new file mode 100644
index 000000000..956a67a9e
--- /dev/null
+++ b/skywalking-sniffer/skywalking-sdk-plugin/mongodb-3.x-plugin/pom.xml
@@ -0,0 +1,47 @@
+
+ 4.0.0
+
+ skywalking-sdk-plugin
+ com.a.eye
+ 3.0.1-2017
+
+
+ skywalking-mongodb-3.x-plugin
+ jar
+
+ mongodb-plugin
+ http://maven.apache.org
+
+
+ UTF-8
+
+
+
+
+ org.mongodb
+ mongo-java-driver
+ 3.4.2
+ provided
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-source-plugin
+
+
+
+ attach-sources
+
+ jar
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/skywalking-sniffer/skywalking-sdk-plugin/mongodb-3.x-plugin/src/main/java/com/a/eye/skywalking/plugin/mongodb/v3/MongoDBMethodInterceptor.java b/skywalking-sniffer/skywalking-sdk-plugin/mongodb-3.x-plugin/src/main/java/com/a/eye/skywalking/plugin/mongodb/v3/MongoDBMethodInterceptor.java
new file mode 100644
index 000000000..d69fabbe8
--- /dev/null
+++ b/skywalking-sniffer/skywalking-sdk-plugin/mongodb-3.x-plugin/src/main/java/com/a/eye/skywalking/plugin/mongodb/v3/MongoDBMethodInterceptor.java
@@ -0,0 +1,190 @@
+package com.a.eye.skywalking.plugin.mongodb.v3;
+
+import java.util.List;
+
+import org.bson.BsonDocument;
+
+import com.a.eye.skywalking.api.conf.Config;
+import com.a.eye.skywalking.api.context.ContextManager;
+import com.a.eye.skywalking.api.plugin.interceptor.EnhancedClassInstanceContext;
+import com.a.eye.skywalking.api.plugin.interceptor.enhance.InstanceMethodInvokeContext;
+import com.a.eye.skywalking.api.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
+import com.a.eye.skywalking.api.plugin.interceptor.enhance.MethodInterceptResult;
+import com.a.eye.skywalking.trace.Span;
+import com.a.eye.skywalking.trace.tag.Tags;
+import com.mongodb.ReadPreference;
+import com.mongodb.bulk.DeleteRequest;
+import com.mongodb.bulk.InsertRequest;
+import com.mongodb.bulk.UpdateRequest;
+import com.mongodb.bulk.WriteRequest;
+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;
+
+/**
+ * {@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 {
+
+ /**
+ * The key name that MongoDB host in {@link EnhancedClassInstanceContext#context}.
+ */
+ static final String MONGODB_HOST = "MONGODB_HOST";
+
+ /**
+ * The key name that MongoDB port in {@link EnhancedClassInstanceContext#context}.
+ */
+ static final String MONGODB_PORT = "MONGODB_PORT";
+
+ private static final String MONGODB_COMPONENT = "MongoDB";
+
+ private static final String METHOD = "MongoDB/";
+
+ private static final int FILTER_LENGTH_LIMIT = 256;
+
+ private static final String EMPTY = "";
+
+ @Override
+ public void beforeMethod(final EnhancedClassInstanceContext context,
+ final InstanceMethodInvokeContext interceptorContext, final MethodInterceptResult result) {
+ Object[] arguments = interceptorContext.allArguments();
+
+ String methodName = arguments[0].getClass().getSimpleName();
+ Span span = ContextManager.createSpan(METHOD + methodName);
+ Tags.COMPONENT.set(span, MONGODB_COMPONENT);
+ Tags.DB_TYPE.set(span, MONGODB_COMPONENT);
+ Tags.SPAN_KIND.set(span, Tags.SPAN_KIND_CLIENT);
+ Tags.SPAN_LAYER.asDB(span);
+
+ if (Config.Plugin.MongoDB.TRACE_PARAM) {
+ Tags.DB_STATEMENT.set(span, methodName + " " + this.getTraceParam(arguments[0]));
+ }
+
+ }
+
+ @Override
+ public Object afterMethod(EnhancedClassInstanceContext context, InstanceMethodInvokeContext interceptorContext,
+ Object ret) {
+ Span span = ContextManager.activeSpan();
+ Tags.PEER_HOST.set(span, context.get(MONGODB_HOST, String.class));
+ Tags.PEER_PORT.set(span, (Integer)context.get(MONGODB_PORT));
+ ContextManager.stopSpan();
+ return ret;
+ }
+
+ @Override
+ public void handleMethodException(Throwable t, EnhancedClassInstanceContext context,
+ InstanceMethodInvokeContext interceptorContext) {
+ ContextManager.activeSpan().log(t);
+ }
+
+ /**
+ * 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 = ((ListCollectionsOperation)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 EMPTY;
+ }
+ }
+
+ private 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(",");
+ }
+ 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;
+ }
+ }
+
+}
diff --git a/skywalking-sniffer/skywalking-sdk-plugin/mongodb-3.x-plugin/src/main/java/com/a/eye/skywalking/plugin/mongodb/v3/MongoDBReadBindingInterceptor.java b/skywalking-sniffer/skywalking-sdk-plugin/mongodb-3.x-plugin/src/main/java/com/a/eye/skywalking/plugin/mongodb/v3/MongoDBReadBindingInterceptor.java
new file mode 100644
index 000000000..000422d5f
--- /dev/null
+++ b/skywalking-sniffer/skywalking-sdk-plugin/mongodb-3.x-plugin/src/main/java/com/a/eye/skywalking/plugin/mongodb/v3/MongoDBReadBindingInterceptor.java
@@ -0,0 +1,49 @@
+package com.a.eye.skywalking.plugin.mongodb.v3;
+
+import com.a.eye.skywalking.api.plugin.interceptor.EnhancedClassInstanceContext;
+import com.a.eye.skywalking.api.plugin.interceptor.enhance.InstanceMethodInvokeContext;
+import com.a.eye.skywalking.api.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
+import com.a.eye.skywalking.api.plugin.interceptor.enhance.MethodInterceptResult;
+import com.mongodb.ReadPreference;
+import com.mongodb.ServerAddress;
+import com.mongodb.binding.ReadBinding;
+
+/**
+ * {@link MongoDBReadBindingInterceptor} record the host and port information
+ * from {@link EnhancedClassInstanceContext#context},
+ *
+ * @author baiyang
+ */
+public class MongoDBReadBindingInterceptor implements
+ InstanceMethodsAroundInterceptor {
+
+ @Override
+ public void beforeMethod(EnhancedClassInstanceContext context,
+ InstanceMethodInvokeContext interceptorContext,
+ MethodInterceptResult result) {
+ }
+
+ /**
+ * Execute after {@link com.mongodb.Mongo#getReadBinding(ReadPreference)},
+ * record the host and port information
+ */
+ @Override
+ public Object afterMethod(EnhancedClassInstanceContext context,
+ InstanceMethodInvokeContext interceptorContext, Object ret) {
+ ReadBinding readBinding = (ReadBinding)ret;
+ ServerAddress serverAddress = readBinding.getReadConnectionSource()
+ .getServerDescription().getAddress();
+ String host = serverAddress.getHost();
+ Integer port = serverAddress.getPort();
+ context.set(MongoDBMethodInterceptor.MONGODB_HOST, host);
+ context.set(MongoDBMethodInterceptor.MONGODB_PORT, port);
+ return ret;
+ }
+
+ @Override
+ public void handleMethodException(Throwable t,
+ EnhancedClassInstanceContext context,
+ InstanceMethodInvokeContext interceptorContext) {
+ }
+
+}
diff --git a/skywalking-sniffer/skywalking-sdk-plugin/mongodb-3.x-plugin/src/main/java/com/a/eye/skywalking/plugin/mongodb/v3/MongoDBWriteBindingInterceptor.java b/skywalking-sniffer/skywalking-sdk-plugin/mongodb-3.x-plugin/src/main/java/com/a/eye/skywalking/plugin/mongodb/v3/MongoDBWriteBindingInterceptor.java
new file mode 100644
index 000000000..09a89f6bc
--- /dev/null
+++ b/skywalking-sniffer/skywalking-sdk-plugin/mongodb-3.x-plugin/src/main/java/com/a/eye/skywalking/plugin/mongodb/v3/MongoDBWriteBindingInterceptor.java
@@ -0,0 +1,44 @@
+package com.a.eye.skywalking.plugin.mongodb.v3;
+
+import com.a.eye.skywalking.api.plugin.interceptor.EnhancedClassInstanceContext;
+import com.a.eye.skywalking.api.plugin.interceptor.enhance.InstanceMethodInvokeContext;
+import com.a.eye.skywalking.api.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
+import com.a.eye.skywalking.api.plugin.interceptor.enhance.MethodInterceptResult;
+import com.mongodb.ServerAddress;
+import com.mongodb.binding.WriteBinding;
+
+/**
+ * {@link MongoDBWriteBindingInterceptor} record the host and port information from {@link
+ * EnhancedClassInstanceContext#context}
+ *
+ * @author baiyang
+ */
+public class MongoDBWriteBindingInterceptor implements InstanceMethodsAroundInterceptor {
+
+ @Override
+ public void beforeMethod(EnhancedClassInstanceContext context, InstanceMethodInvokeContext interceptorContext,
+ MethodInterceptResult result) {
+ }
+
+ /**
+ * Execute after {@link com.mongodb.Mongo#getWriteBinding()},
+ * record the host and port information
+ */
+ @Override
+ public Object afterMethod(EnhancedClassInstanceContext context, InstanceMethodInvokeContext interceptorContext,
+ Object ret) {
+ WriteBinding writeBinding = (WriteBinding)ret;
+ ServerAddress serverAddress = writeBinding.getWriteConnectionSource().getServerDescription().getAddress();
+ String host = serverAddress.getHost();
+ Integer port = serverAddress.getPort();
+ context.set(MongoDBMethodInterceptor.MONGODB_HOST, host);
+ context.set(MongoDBMethodInterceptor.MONGODB_PORT, port);
+ return ret;
+ }
+
+ @Override
+ public void handleMethodException(Throwable t, EnhancedClassInstanceContext context,
+ InstanceMethodInvokeContext interceptorContext) {
+ }
+
+}
diff --git a/skywalking-sniffer/skywalking-sdk-plugin/mongodb-3.x-plugin/src/main/java/com/a/eye/skywalking/plugin/mongodb/v3/define/MongoDBInstrumentation.java b/skywalking-sniffer/skywalking-sdk-plugin/mongodb-3.x-plugin/src/main/java/com/a/eye/skywalking/plugin/mongodb/v3/define/MongoDBInstrumentation.java
new file mode 100644
index 000000000..98f7e08c6
--- /dev/null
+++ b/skywalking-sniffer/skywalking-sdk-plugin/mongodb-3.x-plugin/src/main/java/com/a/eye/skywalking/plugin/mongodb/v3/define/MongoDBInstrumentation.java
@@ -0,0 +1,76 @@
+package com.a.eye.skywalking.plugin.mongodb.v3.define;
+
+import static net.bytebuddy.matcher.ElementMatchers.named;
+
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+
+import com.a.eye.skywalking.api.plugin.interceptor.ConstructorInterceptPoint;
+import com.a.eye.skywalking.api.plugin.interceptor.InstanceMethodsInterceptPoint;
+import com.a.eye.skywalking.api.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
+import com.a.eye.skywalking.plugin.mongodb.v3.MongoDBMethodInterceptor;
+
+/**
+ * {@link MongoDBInstrumentation} presents that skywalking intercepts {@link com.mongodb.Mongo#execute(ReadOperation,
+ * ReadPreference)},{@link com.mongodb.Mongo#execute(WriteOperation)} by using {@link MongoDBMethodInterceptor}.
+ *
+ * @author baiyang
+ */
+public class MongoDBInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
+
+ private static final String ENHANCE_CLASS = "com.mongodb.Mongo";
+
+ private static final String MONGDB_READ_BINDING_CLASS = "com.a.eye.skywalking.plugin.mongodb.v3.MongoDBReadBindingInterceptor";
+
+ private static final String MONGDB_WRITE_BINDING_CLASS = "com.a.eye.skywalking.plugin.mongodb.v3.MongoDBWriteBindingInterceptor";
+
+ private static final String MONGDB_METHOD_INTERCET_CLASS = "com.a.eye.skywalking.plugin.mongodb.v3.MongoDBMethodInterceptor";
+
+ @Override
+ protected ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
+ return null;
+ }
+
+ @Override
+ protected InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
+ return new InstanceMethodsInterceptPoint[] {
+ new InstanceMethodsInterceptPoint() {
+ @Override
+ public ElementMatcher getMethodsMatcher() {
+ return named("execute");
+ }
+
+ @Override
+ public String getMethodsInterceptor() {
+ return MONGDB_METHOD_INTERCET_CLASS;
+ }
+ }, new InstanceMethodsInterceptPoint() {
+ @Override
+ public ElementMatcher getMethodsMatcher() {
+ return named("getReadBinding");
+ }
+
+ @Override
+ public String getMethodsInterceptor() {
+ return MONGDB_READ_BINDING_CLASS;
+ }
+ }, new InstanceMethodsInterceptPoint() {
+ @Override
+ public ElementMatcher getMethodsMatcher() {
+ return named("getWriteBinding");
+ }
+
+ @Override
+ public String getMethodsInterceptor() {
+ return MONGDB_WRITE_BINDING_CLASS;
+ }
+ }
+ };
+ }
+
+ @Override
+ protected String enhanceClassName() {
+ return ENHANCE_CLASS;
+ }
+
+}
diff --git a/skywalking-sniffer/skywalking-sdk-plugin/mongodb-3.x-plugin/src/main/resources/skywalking-plugin.def b/skywalking-sniffer/skywalking-sdk-plugin/mongodb-3.x-plugin/src/main/resources/skywalking-plugin.def
new file mode 100644
index 000000000..8473327cb
--- /dev/null
+++ b/skywalking-sniffer/skywalking-sdk-plugin/mongodb-3.x-plugin/src/main/resources/skywalking-plugin.def
@@ -0,0 +1 @@
+com.a.eye.skywalking.plugin.mongodb.v3.define.MongoDBInstrumentation
\ No newline at end of file
diff --git a/skywalking-sniffer/skywalking-sdk-plugin/mongodb-3.x-plugin/src/test/java/com/a/eye/skywalking/plugin/mongodb/v3/MongoDBMethodInterceptorTest.java b/skywalking-sniffer/skywalking-sdk-plugin/mongodb-3.x-plugin/src/test/java/com/a/eye/skywalking/plugin/mongodb/v3/MongoDBMethodInterceptorTest.java
new file mode 100644
index 000000000..6902ab01c
--- /dev/null
+++ b/skywalking-sniffer/skywalking-sdk-plugin/mongodb-3.x-plugin/src/test/java/com/a/eye/skywalking/plugin/mongodb/v3/MongoDBMethodInterceptorTest.java
@@ -0,0 +1,131 @@
+package com.a.eye.skywalking.plugin.mongodb.v3;
+
+import static org.hamcrest.CoreMatchers.is;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertThat;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Mockito.when;
+
+import org.bson.BsonDocument;
+import org.bson.BsonString;
+import org.bson.codecs.Decoder;
+import org.hamcrest.CoreMatchers;
+import org.hamcrest.MatcherAssert;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.powermock.api.mockito.PowerMockito;
+
+import com.a.eye.skywalking.api.boot.ServiceManager;
+import com.a.eye.skywalking.api.conf.Config;
+import com.a.eye.skywalking.api.context.TracerContext;
+import com.a.eye.skywalking.api.plugin.interceptor.EnhancedClassInstanceContext;
+import com.a.eye.skywalking.api.plugin.interceptor.enhance.InstanceMethodInvokeContext;
+import com.a.eye.skywalking.sniffer.mock.context.MockTracerContextListener;
+import com.a.eye.skywalking.sniffer.mock.context.SegmentAssert;
+import com.a.eye.skywalking.trace.LogData;
+import com.a.eye.skywalking.trace.Span;
+import com.a.eye.skywalking.trace.TraceSegment;
+import com.a.eye.skywalking.trace.tag.Tags;
+import com.mongodb.MongoNamespace;
+import com.mongodb.operation.FindOperation;
+
+@RunWith(MockitoJUnitRunner.class)
+public class MongoDBMethodInterceptorTest {
+
+ private MongoDBMethodInterceptor interceptor;
+ private MockTracerContextListener mockTracerContextListener;
+
+ @Mock
+ private EnhancedClassInstanceContext classInstanceContext;
+ @Mock
+ private InstanceMethodInvokeContext methodInvokeContext;
+
+ @SuppressWarnings({"rawtypes", "unchecked"})
+ @Before
+ public void setUp() throws Exception {
+ ServiceManager.INSTANCE.boot();
+
+ interceptor = new MongoDBMethodInterceptor();
+ mockTracerContextListener = new MockTracerContextListener();
+
+ TracerContext.ListenerManager.add(mockTracerContextListener);
+
+ Config.Plugin.MongoDB.TRACE_PARAM = true;
+
+ when(classInstanceContext.get(MongoDBMethodInterceptor.MONGODB_HOST, String.class)).thenReturn("127.0.0.1");
+ when(classInstanceContext.get(MongoDBMethodInterceptor.MONGODB_PORT)).thenReturn(27017);
+ when(methodInvokeContext.methodName()).thenReturn("find");
+
+ 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);
+
+ when(methodInvokeContext.allArguments()).thenReturn(new Object[] {findOperation});
+ }
+
+ @Test
+ public void testIntercept() {
+ interceptor.beforeMethod(classInstanceContext, methodInvokeContext, null);
+ interceptor.afterMethod(classInstanceContext, methodInvokeContext, null);
+
+ mockTracerContextListener.assertSize(1);
+ mockTracerContextListener.assertTraceSegment(0, new SegmentAssert() {
+ @Override
+ public void call(TraceSegment traceSegment) {
+ assertThat(traceSegment.getSpans().size(), is(1));
+ Span span = traceSegment.getSpans().get(0);
+ assertRedisSpan(span);
+ }
+ });
+ }
+
+ @Test
+ public void testInterceptWithException() {
+ interceptor.beforeMethod(classInstanceContext, methodInvokeContext, null);
+ interceptor.handleMethodException(new RuntimeException(), classInstanceContext, methodInvokeContext);
+ interceptor.afterMethod(classInstanceContext, methodInvokeContext, null);
+
+ mockTracerContextListener.assertSize(1);
+ mockTracerContextListener.assertTraceSegment(0, new SegmentAssert() {
+ @Override
+ public void call(TraceSegment traceSegment) {
+ assertThat(traceSegment.getSpans().size(), is(1));
+ Span span = traceSegment.getSpans().get(0);
+ assertRedisSpan(span);
+ assertThat(span.getLogs().size(), is(1));
+ assertLogData(span.getLogs().get(0));
+ }
+ });
+ }
+
+ private void assertLogData(LogData logData) {
+ MatcherAssert.assertThat(logData.getFields().size(), is(4));
+ MatcherAssert.assertThat(logData.getFields().get("event"), CoreMatchers.