From e61cc13621fa7605a18bdf815257470bd4cbb745 Mon Sep 17 00:00:00 2001
From: IluckySi <1151262684@qq.com>
Date: Sun, 8 Oct 2017 15:08:09 +0800
Subject: [PATCH] Support SpyMemcached V2.x
---
.../trace/component/ComponentsDefine.java | 11 +-
apm-sniffer/apm-agent/pom.xml | 5 +
apm-sniffer/apm-sdk-plugin/pom.xml | 1 +
.../spymemcached-2.x-plugin/pom.xml | 35 +++++
...thInetSocketAddressListArgInterceptor.java | 26 ++++
.../v2/MemcachedMethodInterceptor.java | 42 ++++++
.../v2/define/MemcachedInstrumentation.java | 82 ++++++++++++
.../src/main/resources/skywalking-plugin.def | 1 +
...etSocketAddressListArgInterceptorTest.java | 39 ++++++
.../v2/MemcachedMethodInterceptorTest.java | 121 ++++++++++++++++++
10 files changed, 359 insertions(+), 4 deletions(-)
create mode 100644 apm-sniffer/apm-sdk-plugin/spymemcached-2.x-plugin/pom.xml
create mode 100644 apm-sniffer/apm-sdk-plugin/spymemcached-2.x-plugin/src/main/java/org/skywalking/apm/plugin/spymemcached/v2/MemcachedConstructorWithInetSocketAddressListArgInterceptor.java
create mode 100644 apm-sniffer/apm-sdk-plugin/spymemcached-2.x-plugin/src/main/java/org/skywalking/apm/plugin/spymemcached/v2/MemcachedMethodInterceptor.java
create mode 100644 apm-sniffer/apm-sdk-plugin/spymemcached-2.x-plugin/src/main/java/org/skywalking/apm/plugin/spymemcached/v2/define/MemcachedInstrumentation.java
create mode 100644 apm-sniffer/apm-sdk-plugin/spymemcached-2.x-plugin/src/main/resources/skywalking-plugin.def
create mode 100644 apm-sniffer/apm-sdk-plugin/spymemcached-2.x-plugin/src/test/java/org/skywalking/apm/plugin/spymemcached/v2/MemcachedConstructorWithInetSocketAddressListArgInterceptorTest.java
create mode 100644 apm-sniffer/apm-sdk-plugin/spymemcached-2.x-plugin/src/test/java/org/skywalking/apm/plugin/spymemcached/v2/MemcachedMethodInterceptorTest.java
diff --git a/apm-network/src/main/java/org/skywalking/apm/network/trace/component/ComponentsDefine.java b/apm-network/src/main/java/org/skywalking/apm/network/trace/component/ComponentsDefine.java
index b37078faa..c635c2d2e 100644
--- a/apm-network/src/main/java/org/skywalking/apm/network/trace/component/ComponentsDefine.java
+++ b/apm-network/src/main/java/org/skywalking/apm/network/trace/component/ComponentsDefine.java
@@ -43,8 +43,10 @@ public class ComponentsDefine {
public static final OfficialComponent JETTY_CLIENT = new OfficialComponent(18, "JettyClient");
- public static final OfficialComponent JETTY_SERVER = new OfficialComponent(19, "JettyServer");
-
+ public static final OfficialComponent JETTY_SERVER = new OfficialComponent(19, "JettyServer");
+
+ public static final OfficialComponent MEMCACHE = new OfficialComponent(20, "Memcache");
+
private static ComponentsDefine instance = new ComponentsDefine();
private String[] components;
@@ -54,7 +56,7 @@ public class ComponentsDefine {
}
public ComponentsDefine() {
- components = new String[20];
+ components = new String[21];
addComponent(TOMCAT);
addComponent(HTTPCLIENT);
addComponent(DUBBO);
@@ -74,6 +76,7 @@ public class ComponentsDefine {
addComponent(NUTZ_HTTP);
addComponent(JETTY_CLIENT);
addComponent(JETTY_SERVER);
+ addComponent(MEMCACHE);
}
private void addComponent(OfficialComponent component) {
@@ -87,4 +90,4 @@ public class ComponentsDefine {
return components[componentId];
}
}
-}
+}
\ No newline at end of file
diff --git a/apm-sniffer/apm-agent/pom.xml b/apm-sniffer/apm-agent/pom.xml
index de97031ce..064e0917c 100644
--- a/apm-sniffer/apm-agent/pom.xml
+++ b/apm-sniffer/apm-agent/pom.xml
@@ -125,6 +125,11 @@
apm-jetty-server-9.x-plugin
${project.version}
+
+ org.skywalking
+ apm-spymemcached-2.x-plugin
+ ${project.version}
+
diff --git a/apm-sniffer/apm-sdk-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/pom.xml
index 976ca663c..145822656 100644
--- a/apm-sniffer/apm-sdk-plugin/pom.xml
+++ b/apm-sniffer/apm-sdk-plugin/pom.xml
@@ -26,6 +26,7 @@
struts2-2.x-plugin
nutz-plugins
jetty-plugin
+ spymemcached-2.x-plugin
pom
diff --git a/apm-sniffer/apm-sdk-plugin/spymemcached-2.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/spymemcached-2.x-plugin/pom.xml
new file mode 100644
index 000000000..784547130
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/spymemcached-2.x-plugin/pom.xml
@@ -0,0 +1,35 @@
+
+
+ 4.0.0
+
+ org.skywalking
+ apm-sdk-plugin
+ 3.2.3-2017
+
+
+ apm-spymemcached-2.x-plugin
+ spymemcached-2.x-plugin
+ http://maven.apache.org
+
+
+ UTF-8
+ 2.11.1
+
+
+
+
+ net.spy
+ spymemcached
+ ${spymemcached.version}
+ provided
+
+
+ org.apache.logging.log4j
+ log4j-core
+ 2.4.1
+ test
+
+
+
\ No newline at end of file
diff --git a/apm-sniffer/apm-sdk-plugin/spymemcached-2.x-plugin/src/main/java/org/skywalking/apm/plugin/spymemcached/v2/MemcachedConstructorWithInetSocketAddressListArgInterceptor.java b/apm-sniffer/apm-sdk-plugin/spymemcached-2.x-plugin/src/main/java/org/skywalking/apm/plugin/spymemcached/v2/MemcachedConstructorWithInetSocketAddressListArgInterceptor.java
new file mode 100644
index 000000000..f7c9d1716
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/spymemcached-2.x-plugin/src/main/java/org/skywalking/apm/plugin/spymemcached/v2/MemcachedConstructorWithInetSocketAddressListArgInterceptor.java
@@ -0,0 +1,26 @@
+package org.skywalking.apm.plugin.spymemcached.v2;
+
+import java.net.InetSocketAddress;
+import java.util.List;
+
+import org.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import org.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor;
+
+public class MemcachedConstructorWithInetSocketAddressListArgInterceptor implements InstanceConstructorInterceptor {
+
+ @Override
+ public void onConstruct(EnhancedInstance objInst, Object[] allArguments) {
+ StringBuilder memcachConnInfo = new StringBuilder();
+ @SuppressWarnings("unchecked")
+ List inetSocketAddressList = (List)allArguments[1];
+ for (InetSocketAddress inetSocketAddress : inetSocketAddressList) {
+ String host = inetSocketAddress.getAddress().getHostAddress();
+ int port = inetSocketAddress.getPort();
+ memcachConnInfo.append(host).append(":").append(port).append(";");
+ }
+ if (memcachConnInfo.length() > 1) {
+ memcachConnInfo = new StringBuilder(memcachConnInfo.substring(0, memcachConnInfo.length() - 1));
+ }
+ objInst.setSkyWalkingDynamicField(memcachConnInfo.toString());
+ }
+}
\ No newline at end of file
diff --git a/apm-sniffer/apm-sdk-plugin/spymemcached-2.x-plugin/src/main/java/org/skywalking/apm/plugin/spymemcached/v2/MemcachedMethodInterceptor.java b/apm-sniffer/apm-sdk-plugin/spymemcached-2.x-plugin/src/main/java/org/skywalking/apm/plugin/spymemcached/v2/MemcachedMethodInterceptor.java
new file mode 100644
index 000000000..89a864dab
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/spymemcached-2.x-plugin/src/main/java/org/skywalking/apm/plugin/spymemcached/v2/MemcachedMethodInterceptor.java
@@ -0,0 +1,42 @@
+package org.skywalking.apm.plugin.spymemcached.v2;
+
+import java.lang.reflect.Method;
+
+import org.skywalking.apm.agent.core.context.ContextManager;
+import org.skywalking.apm.agent.core.context.tag.Tags;
+import org.skywalking.apm.agent.core.context.trace.AbstractSpan;
+import org.skywalking.apm.agent.core.context.trace.SpanLayer;
+import org.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import org.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
+import org.skywalking.apm.agent.core.plugin.interceptor.enhance.MethodInterceptResult;
+import org.skywalking.apm.network.trace.component.ComponentsDefine;
+
+public class MemcachedMethodInterceptor implements InstanceMethodsAroundInterceptor {
+
+ private static final String SPY_MEMCACHE = "SpyMemcached/";
+
+ @Override
+ public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments,
+ Class>[] argumentsTypes, MethodInterceptResult result) throws Throwable {
+ String peer = String.valueOf(objInst.getSkyWalkingDynamicField());
+ AbstractSpan span = ContextManager.createExitSpan(SPY_MEMCACHE + method.getName(), peer);
+ span.setComponent(ComponentsDefine.MEMCACHE);
+ Tags.DB_TYPE.set(span, ComponentsDefine.MEMCACHE.getName());
+ SpanLayer.asDB(span);
+ Tags.DB_STATEMENT.set(span, method.getName() + " " + allArguments[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 span = ContextManager.activeSpan();
+ span.errorOccurred();
+ span.log(t);
+ }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/spymemcached-2.x-plugin/src/main/java/org/skywalking/apm/plugin/spymemcached/v2/define/MemcachedInstrumentation.java b/apm-sniffer/apm-sdk-plugin/spymemcached-2.x-plugin/src/main/java/org/skywalking/apm/plugin/spymemcached/v2/define/MemcachedInstrumentation.java
new file mode 100644
index 000000000..bbb18baab
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/spymemcached-2.x-plugin/src/main/java/org/skywalking/apm/plugin/spymemcached/v2/define/MemcachedInstrumentation.java
@@ -0,0 +1,82 @@
+package org.skywalking.apm.plugin.spymemcached.v2.define;
+
+import static org.skywalking.apm.agent.core.plugin.match.NameMatch.byName;
+
+import java.util.List;
+
+import org.skywalking.apm.agent.core.plugin.interceptor.ConstructorInterceptPoint;
+import org.skywalking.apm.agent.core.plugin.interceptor.InstanceMethodsInterceptPoint;
+import org.skywalking.apm.agent.core.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
+import org.skywalking.apm.agent.core.plugin.match.ClassMatch;
+
+import net.bytebuddy.description.method.MethodDescription;
+import net.bytebuddy.matcher.ElementMatcher;
+
+import static net.bytebuddy.matcher.ElementMatchers.named;
+import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
+
+/**
+ *
+ * {@link MemcachedInstrumentation} presents that skywalking intercept all constructors and methods of
+ * {@link net.spy.memcached.MemcachedClient}.
+ * {@link XMemcachedConstructorWithInetSocketAddressListArgInterceptor} intercepts the constructor with
+ * argument {@link java.net.InetSocketAddress}.
+ *
+ * @author IluckySi
+ *
+ */
+public class MemcachedInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
+
+ private static final String ENHANCE_CLASS = "net.spy.memcached.MemcachedClient";
+ private static final String CONSTRUCTOR_WITH_INETSOCKETADDRESS_LIST_ARG_INTERCEPT_CLASS = "org.skywalking.apm.plugin.spymemcached.v2.MemcachedConstructorWithInetSocketAddressListArgInterceptor";
+ private static final String METHOD_INTERCEPT_CLASS = "org.skywalking.apm.plugin.spymemcached.v2.MemcachedMethodInterceptor";
+
+ @Override
+ public ClassMatch enhanceClass() {
+ return byName(ENHANCE_CLASS);
+ }
+
+ @Override
+ protected ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
+ return new ConstructorInterceptPoint[] {
+ new ConstructorInterceptPoint() {
+ @Override
+ public ElementMatcher getConstructorMatcher() {
+ return takesArgument(1, List.class);
+ }
+
+ @Override
+ public String getConstructorInterceptor() {
+ return CONSTRUCTOR_WITH_INETSOCKETADDRESS_LIST_ARG_INTERCEPT_CLASS;
+ }
+ }
+ };
+ }
+
+ @Override
+ protected InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
+ return new InstanceMethodsInterceptPoint[] {
+ new InstanceMethodsInterceptPoint() {
+ @Override
+ public ElementMatcher getMethodsMatcher() {
+ return named("touch").or(named("append")) .or(named("prepend")).or(named("asyncCAS"))
+ .or(named("cas")) .or(named("add")).or(named("set")).or(named("replace"))
+ .or(named("asyncGet")).or(named("asyncGets")).or(named("gets")).or(named("getAndTouch"))
+ .or(named("get")).or(named("asyncGetBulk")) .or(named("asyncGetAndTouch"))
+ .or(named("getBulk")).or(named("getStats")) .or(named("incr"))
+ .or(named("decr")).or(named("asyncIncr")) .or(named("asyncDecr"))
+ .or(named("delete"));
+ }
+
+ @Override
+ public String getMethodsInterceptor() {
+ return METHOD_INTERCEPT_CLASS;
+ }
+
+ @Override public boolean isOverrideArgs() {
+ return false;
+ }
+ }
+ };
+ }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/spymemcached-2.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/spymemcached-2.x-plugin/src/main/resources/skywalking-plugin.def
new file mode 100644
index 000000000..f552f1602
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/spymemcached-2.x-plugin/src/main/resources/skywalking-plugin.def
@@ -0,0 +1 @@
+spymemcached-2.x=org.skywalking.apm.plugin.spymemcached.v2.define.MemcachedInstrumentation
\ No newline at end of file
diff --git a/apm-sniffer/apm-sdk-plugin/spymemcached-2.x-plugin/src/test/java/org/skywalking/apm/plugin/spymemcached/v2/MemcachedConstructorWithInetSocketAddressListArgInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/spymemcached-2.x-plugin/src/test/java/org/skywalking/apm/plugin/spymemcached/v2/MemcachedConstructorWithInetSocketAddressListArgInterceptorTest.java
new file mode 100644
index 000000000..d4238a200
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/spymemcached-2.x-plugin/src/test/java/org/skywalking/apm/plugin/spymemcached/v2/MemcachedConstructorWithInetSocketAddressListArgInterceptorTest.java
@@ -0,0 +1,39 @@
+package org.skywalking.apm.plugin.spymemcached.v2;
+
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import java.net.InetSocketAddress;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.runners.MockitoJUnitRunner;
+import org.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+
+@RunWith(MockitoJUnitRunner.class)
+public class MemcachedConstructorWithInetSocketAddressListArgInterceptorTest {
+
+ private MemcachedConstructorWithInetSocketAddressListArgInterceptor interceptor;
+
+ @Mock
+ private EnhancedInstance enhancedInstance;
+
+ @Before
+ public void setUp() throws Exception {
+ interceptor = new MemcachedConstructorWithInetSocketAddressListArgInterceptor();
+ }
+
+ @Test
+ public void onConstructWithInetSocketAddressList() {
+ List inetSocketAddressList = new ArrayList();
+ inetSocketAddressList.add(new InetSocketAddress("127.0.0.1", 11211));
+ inetSocketAddressList.add(new InetSocketAddress("127.0.0.2", 11211));
+ interceptor.onConstruct(enhancedInstance, new Object[]{null, inetSocketAddressList});
+
+ verify(enhancedInstance, times(1)).setSkyWalkingDynamicField("127.0.0.1:11211;127.0.0.2:11211");
+ }
+}
diff --git a/apm-sniffer/apm-sdk-plugin/spymemcached-2.x-plugin/src/test/java/org/skywalking/apm/plugin/spymemcached/v2/MemcachedMethodInterceptorTest.java b/apm-sniffer/apm-sdk-plugin/spymemcached-2.x-plugin/src/test/java/org/skywalking/apm/plugin/spymemcached/v2/MemcachedMethodInterceptorTest.java
new file mode 100644
index 000000000..3ddc3be8c
--- /dev/null
+++ b/apm-sniffer/apm-sdk-plugin/spymemcached-2.x-plugin/src/test/java/org/skywalking/apm/plugin/spymemcached/v2/MemcachedMethodInterceptorTest.java
@@ -0,0 +1,121 @@
+package org.skywalking.apm.plugin.spymemcached.v2;
+
+import static junit.framework.TestCase.assertNotNull;
+import static org.hamcrest.CoreMatchers.is;
+import static org.hamcrest.MatcherAssert.assertThat;
+import static org.mockito.Mockito.when;
+
+import java.lang.reflect.Method;
+import java.util.List;
+
+import org.hamcrest.CoreMatchers;
+import org.junit.Assert;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.powermock.modules.junit4.PowerMockRunner;
+import org.powermock.modules.junit4.PowerMockRunnerDelegate;
+import org.skywalking.apm.agent.core.context.trace.AbstractTracingSpan;
+import org.skywalking.apm.agent.core.context.trace.LogDataEntity;
+import org.skywalking.apm.agent.core.context.trace.SpanLayer;
+import org.skywalking.apm.agent.core.context.trace.TraceSegment;
+import org.skywalking.apm.agent.core.context.util.KeyValuePair;
+import org.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance;
+import org.skywalking.apm.agent.test.helper.SegmentHelper;
+import org.skywalking.apm.agent.test.helper.SpanHelper;
+import org.skywalking.apm.agent.test.tools.AgentServiceRule;
+import org.skywalking.apm.agent.test.tools.SegmentStorage;
+import org.skywalking.apm.agent.test.tools.SegmentStoragePoint;
+import org.skywalking.apm.agent.test.tools.TracingSegmentRunner;
+
+import net.spy.memcached.MemcachedClient;
+
+@RunWith(PowerMockRunner.class)
+@PowerMockRunnerDelegate(TracingSegmentRunner.class)
+public class MemcachedMethodInterceptorTest {
+
+ @SegmentStoragePoint
+ private SegmentStorage segmentStorage;
+ @Rule
+ public AgentServiceRule serviceRule = new AgentServiceRule();
+ @Mock
+ private EnhancedInstance enhancedInstance;
+ private MemcachedMethodInterceptor interceptor;
+
+ private Object[] allArgument;
+ private Class[] argumentType;
+
+ @Before
+ public void setUp() throws Exception {
+ allArgument = new Object[] {"OperationKey", "OperationValue"};
+ argumentType = new Class[] {String.class, String.class};
+
+ interceptor = new MemcachedMethodInterceptor();
+ when(enhancedInstance.getSkyWalkingDynamicField()).thenReturn("127.0.0.1:11211");
+ }
+
+ @Test
+ public void testIntercept() throws Throwable {
+ interceptor.beforeMethod(enhancedInstance, getMockSetMethod(), allArgument, argumentType, null);
+ interceptor.afterMethod(enhancedInstance, getMockGetMethod(), allArgument, argumentType, null);
+
+ TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0);
+ List spans = SegmentHelper.getSpans(traceSegment);
+ assertThat(spans.size(), is(1));
+ assertMemcacheSpan(spans.get(0));
+ }
+
+ @Test
+ public void testInterceptWithException() throws Throwable {
+ interceptor.beforeMethod(enhancedInstance, getMockSetMethod(), allArgument, argumentType, null);
+ interceptor.handleMethodException(enhancedInstance, getMockSetMethod(), allArgument, argumentType, new RuntimeException());
+ interceptor.afterMethod(enhancedInstance, getMockSetMethod(), allArgument, argumentType, null);
+
+ TraceSegment traceSegment = segmentStorage.getTraceSegments().get(0);
+ List spans = SegmentHelper.getSpans(traceSegment);
+ assertThat(spans.size(), is(1));
+ assertMemcacheSpan(spans.get(0));
+
+ assertLogData(SpanHelper.getLogs(spans.get(0)));
+ }
+
+ private void assertLogData(List logDataEntities) {
+ assertThat(logDataEntities.size(), is(1));
+ LogDataEntity logData = logDataEntities.get(0);
+ Assert.assertThat(logData.getLogs().size(), is(4));
+ Assert.assertThat(logData.getLogs().get(0).getValue(), CoreMatchers.