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.is("error")); + Assert.assertThat(logData.getLogs().get(1).getValue(), CoreMatchers.is(RuntimeException.class.getName())); + Assert.assertNull(logData.getLogs().get(2).getValue()); + assertNotNull(logData.getLogs().get(3).getValue()); + } + + private void assertMemcacheSpan(AbstractTracingSpan span) { + assertThat(span.getOperationName(), is("SpyMemcached/set")); + assertThat(span.isExit(), is(true)); + assertThat(SpanHelper.getComponentId(span), is(20)); + List tags = SpanHelper.getTags(span); + assertThat(tags.get(0).getValue(), is("Memcache")); + assertThat(tags.get(1).getValue(), is("set OperationKey")); + assertThat(SpanHelper.getLayer(span), is(SpanLayer.DB)); + } + + private Method getMockSetMethod() { + try { + return MemcachedClient.class.getMethod("set", String.class, int.class, Object.class); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + return null; + } + } + + private Method getMockGetMethod() { + try { + return MemcachedClient.class.getMethod("get", String.class); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + return null; + } + } +} -- GitLab