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 912efa40c..86a684b59 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 @@ -31,6 +31,10 @@ public class ComponentsDefine { public static final OfficialComponent OKHTTP = new OfficialComponent(12, "OKHttp"); + public static final OfficialComponent SPRING_REST_TEMPLATE = new OfficialComponent(13, "SpringRestTemplate"); + + public static final OfficialComponent SPRING_MVC_ANNOTATION = new OfficialComponent(14, "SpringMVCAnnotation"); + private static ComponentsDefine instance = new ComponentsDefine(); private String[] components; @@ -40,7 +44,7 @@ public class ComponentsDefine { } public ComponentsDefine() { - components = new String[13]; + components = new String[15]; addComponent(TOMCAT); addComponent(HTTPCLIENT); addComponent(DUBBO); @@ -53,6 +57,8 @@ public class ComponentsDefine { addComponent(RESIN); addComponent(FEIGN); addComponent(OKHTTP); + addComponent(SPRING_REST_TEMPLATE); + addComponent(SPRING_MVC_ANNOTATION); } private void addComponent(OfficialComponent component) { diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/skywalking/apm/agent/core/context/TracingContext.java b/apm-sniffer/apm-agent-core/src/main/java/org/skywalking/apm/agent/core/context/TracingContext.java index 10efeb9b4..c6a8f9622 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/skywalking/apm/agent/core/context/TracingContext.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/skywalking/apm/agent/core/context/TracingContext.java @@ -290,19 +290,19 @@ public class TracingContext implements AbstractTracerContext { .find(remotePeer).doInCondition( new PossibleFound.FoundAndObtain() { @Override - public Object doProcess(final int applicationId) { + public Object doProcess(final int peerId) { return DictionaryManager.findOperationNameCodeSection() - .findOrPrepare4Register(applicationId, operationName) + .findOnly(segment.getApplicationId(), operationName) .doInCondition( new PossibleFound.FoundAndObtain() { @Override public Object doProcess(int operationId) { - return new ExitSpan(spanIdGenerator++, parentSpanId, operationId, applicationId); + return new ExitSpan(spanIdGenerator++, parentSpanId, operationId, peerId); } }, new PossibleFound.NotFoundAndObtain() { @Override public Object doProcess() { - return new ExitSpan(spanIdGenerator++, parentSpanId, operationName, remotePeer); + return new ExitSpan(spanIdGenerator++, parentSpanId, operationName, peerId); } }); } @@ -310,7 +310,20 @@ public class TracingContext implements AbstractTracerContext { new PossibleFound.NotFoundAndObtain() { @Override public Object doProcess() { - return new ExitSpan(spanIdGenerator++, parentSpanId, operationName, remotePeer); + return DictionaryManager.findOperationNameCodeSection() + .findOnly(segment.getApplicationId(), operationName) + .doInCondition( + new PossibleFound.FoundAndObtain() { + @Override + public Object doProcess(int operationId) { + return new ExitSpan(spanIdGenerator++, parentSpanId, operationId, remotePeer); + } + }, new PossibleFound.NotFoundAndObtain() { + @Override + public Object doProcess() { + return new ExitSpan(spanIdGenerator++, parentSpanId, operationName, remotePeer); + } + }); } }); push(exitSpan); diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/skywalking/apm/agent/core/context/trace/EntrySpan.java b/apm-sniffer/apm-agent-core/src/main/java/org/skywalking/apm/agent/core/context/trace/EntrySpan.java index 4b2d69cfc..13ebe887e 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/skywalking/apm/agent/core/context/trace/EntrySpan.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/skywalking/apm/agent/core/context/trace/EntrySpan.java @@ -1,8 +1,6 @@ package org.skywalking.apm.agent.core.context.trace; -import org.skywalking.apm.agent.core.dictionary.DictionaryManager; import org.skywalking.apm.agent.core.dictionary.DictionaryUtil; -import org.skywalking.apm.agent.core.dictionary.PossibleFound; import org.skywalking.apm.network.trace.component.Component; /** @@ -18,19 +16,16 @@ import org.skywalking.apm.network.trace.component.Component; * * @author wusheng */ -public class EntrySpan extends AbstractTracingSpan { - private int stackDepth; +public class EntrySpan extends StackBasedTracingSpan { private int currentMaxDepth; public EntrySpan(int spanId, int parentSpanId, String operationName) { super(spanId, parentSpanId, operationName); - this.stackDepth = 0; this.currentMaxDepth = 0; } public EntrySpan(int spanId, int parentSpanId, int operationId) { super(spanId, parentSpanId, operationId); - this.stackDepth = 0; this.currentMaxDepth = 0; } @@ -81,31 +76,6 @@ public class EntrySpan extends AbstractTracingSpan { } } - @Override - public boolean finish(TraceSegment owner) { - if (--stackDepth == 0) { - if (this.operationId == DictionaryUtil.nullValue()) { - this.operationId = (Integer)DictionaryManager.findOperationNameCodeSection() - .findOrPrepare4Register(owner.getApplicationId(), operationName) - .doInCondition( - new PossibleFound.FoundAndObtain() { - @Override public Object doProcess(int value) { - return value; - } - }, - new PossibleFound.NotFoundAndObtain() { - @Override public Object doProcess() { - return DictionaryUtil.nullValue(); - } - } - ); - } - return super.finish(owner); - } else { - return false; - } - } - @Override public AbstractTracingSpan setOperationName(String operationName) { if (stackDepth == currentMaxDepth) { diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/skywalking/apm/agent/core/context/trace/ExitSpan.java b/apm-sniffer/apm-agent-core/src/main/java/org/skywalking/apm/agent/core/context/trace/ExitSpan.java index bed47ff12..de9851804 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/skywalking/apm/agent/core/context/trace/ExitSpan.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/skywalking/apm/agent/core/context/trace/ExitSpan.java @@ -17,32 +17,34 @@ import org.skywalking.apm.network.trace.component.Component; * * @author wusheng */ -public class ExitSpan extends AbstractTracingSpan { - private int stackDepth; +public class ExitSpan extends StackBasedTracingSpan { private String peer; private int peerId; public ExitSpan(int spanId, int parentSpanId, String operationName, String peer) { super(spanId, parentSpanId, operationName); - this.stackDepth = 0; this.peer = peer; this.peerId = DictionaryUtil.nullValue(); } public ExitSpan(int spanId, int parentSpanId, int operationId, int peerId) { super(spanId, parentSpanId, operationId); - this.stackDepth = 0; this.peer = null; this.peerId = peerId; } public ExitSpan(int spanId, int parentSpanId, int operationId, String peer) { super(spanId, parentSpanId, operationId); - this.stackDepth = 0; this.peer = peer; this.peerId = DictionaryUtil.nullValue(); } + public ExitSpan(int spanId, int parentSpanId, String operationName, int peerId) { + super(spanId, parentSpanId, operationName); + this.peer = null; + this.peerId = peerId; + } + /** * Set the {@link #startTime}, when the first start, which means the first service provided. */ @@ -62,15 +64,6 @@ public class ExitSpan extends AbstractTracingSpan { return this; } - @Override - public boolean finish(TraceSegment owner) { - if (--stackDepth == 0) { - return super.finish(owner); - } else { - return false; - } - } - @Override public AbstractTracingSpan setLayer(SpanLayer layer) { if (stackDepth == 1) { diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/skywalking/apm/agent/core/context/trace/StackBasedTracingSpan.java b/apm-sniffer/apm-agent-core/src/main/java/org/skywalking/apm/agent/core/context/trace/StackBasedTracingSpan.java new file mode 100644 index 000000000..846e195ed --- /dev/null +++ b/apm-sniffer/apm-agent-core/src/main/java/org/skywalking/apm/agent/core/context/trace/StackBasedTracingSpan.java @@ -0,0 +1,51 @@ +package org.skywalking.apm.agent.core.context.trace; + +import org.skywalking.apm.agent.core.dictionary.DictionaryManager; +import org.skywalking.apm.agent.core.dictionary.DictionaryUtil; +import org.skywalking.apm.agent.core.dictionary.PossibleFound; + +/** + * The StackBasedTracingSpan represents a span with an inside stack construction. + * + * This kind of span can start and finish multi times in a stack-like invoke line. + * + * @author wusheng + */ +public abstract class StackBasedTracingSpan extends AbstractTracingSpan { + protected int stackDepth; + + protected StackBasedTracingSpan(int spanId, int parentSpanId, String operationName) { + super(spanId, parentSpanId, operationName); + this.stackDepth = 0; + } + + protected StackBasedTracingSpan(int spanId, int parentSpanId, int operationId) { + super(spanId, parentSpanId, operationId); + this.stackDepth = 0; + } + + @Override + public boolean finish(TraceSegment owner) { + if (--stackDepth == 0) { + if (this.operationId == DictionaryUtil.nullValue()) { + this.operationId = (Integer)DictionaryManager.findOperationNameCodeSection() + .findOrPrepare4Register(owner.getApplicationId(), operationName) + .doInCondition( + new PossibleFound.FoundAndObtain() { + @Override public Object doProcess(int value) { + return value; + } + }, + new PossibleFound.NotFoundAndObtain() { + @Override public Object doProcess() { + return DictionaryUtil.nullValue(); + } + } + ); + } + return super.finish(owner); + } else { + return false; + } + } +} diff --git a/apm-sniffer/apm-agent-core/src/main/java/org/skywalking/apm/agent/core/plugin/match/ClassAnnotationMatch.java b/apm-sniffer/apm-agent-core/src/main/java/org/skywalking/apm/agent/core/plugin/match/ClassAnnotationMatch.java index 3cc7aabb0..894e37dfb 100644 --- a/apm-sniffer/apm-agent-core/src/main/java/org/skywalking/apm/agent/core/plugin/match/ClassAnnotationMatch.java +++ b/apm-sniffer/apm-agent-core/src/main/java/org/skywalking/apm/agent/core/plugin/match/ClassAnnotationMatch.java @@ -1,5 +1,6 @@ package org.skywalking.apm.agent.core.plugin.match; +import java.util.ArrayList; import java.util.Arrays; import java.util.List; import net.bytebuddy.description.annotation.AnnotationDescription; @@ -43,7 +44,7 @@ public class ClassAnnotationMatch implements IndirectMatch { @Override public boolean isMatch(TypeDescription typeDescription) { - List annotationList = Arrays.asList(annotations); + List annotationList = new ArrayList(Arrays.asList(annotations)); AnnotationList declaredAnnotations = typeDescription.getDeclaredAnnotations(); for (AnnotationDescription annotation : declaredAnnotations) { annotationList.remove(annotation.getAnnotationType().getActualName()); diff --git a/apm-sniffer/apm-agent-core/src/test/java/org/skywalking/apm/agent/core/context/util/AbstractTracingSpanHelper.java b/apm-sniffer/apm-agent-core/src/test/java/org/skywalking/apm/agent/core/context/util/AbstractTracingSpanHelper.java index c16872e0a..44eaf57a2 100644 --- a/apm-sniffer/apm-agent-core/src/test/java/org/skywalking/apm/agent/core/context/util/AbstractTracingSpanHelper.java +++ b/apm-sniffer/apm-agent-core/src/test/java/org/skywalking/apm/agent/core/context/util/AbstractTracingSpanHelper.java @@ -8,7 +8,7 @@ import org.skywalking.apm.agent.core.context.trace.LogDataEntity; public class AbstractTracingSpanHelper { public static int getParentSpanId(AbstractTracingSpan tracingSpan) { try { - return FieldGetter.getParentFieldValue(tracingSpan, "parentSpanId"); + return FieldGetter.get2LevelParentFieldValue(tracingSpan, "parentSpanId"); } catch (Exception e) { } @@ -17,7 +17,7 @@ public class AbstractTracingSpanHelper { public static List getLogs(AbstractTracingSpan tracingSpan) { try { - return FieldGetter.getParentFieldValue(tracingSpan, "logs"); + return FieldGetter.get2LevelParentFieldValue(tracingSpan, "logs"); } catch (Exception e) { } diff --git a/apm-sniffer/apm-agent-core/src/test/java/org/skywalking/apm/agent/core/context/util/FieldGetter.java b/apm-sniffer/apm-agent-core/src/test/java/org/skywalking/apm/agent/core/context/util/FieldGetter.java index 0aeaad0b9..45c00518f 100644 --- a/apm-sniffer/apm-agent-core/src/test/java/org/skywalking/apm/agent/core/context/util/FieldGetter.java +++ b/apm-sniffer/apm-agent-core/src/test/java/org/skywalking/apm/agent/core/context/util/FieldGetter.java @@ -10,9 +10,10 @@ public class FieldGetter { return (T)field.get(instance); } - public static T getParentFieldValue(Object instance, + + public static T get2LevelParentFieldValue(Object instance, String fieldName) throws IllegalAccessException, NoSuchFieldException { - Field field = instance.getClass().getSuperclass().getDeclaredField(fieldName); + Field field = instance.getClass().getSuperclass().getSuperclass().getDeclaredField(fieldName); field.setAccessible(true); return (T)field.get(instance); } diff --git a/apm-sniffer/apm-agent-core/src/test/java/org/skywalking/apm/agent/core/context/util/SpanHelper.java b/apm-sniffer/apm-agent-core/src/test/java/org/skywalking/apm/agent/core/context/util/SpanHelper.java index 1ab112344..802b74215 100644 --- a/apm-sniffer/apm-agent-core/src/test/java/org/skywalking/apm/agent/core/context/util/SpanHelper.java +++ b/apm-sniffer/apm-agent-core/src/test/java/org/skywalking/apm/agent/core/context/util/SpanHelper.java @@ -7,7 +7,7 @@ public class SpanHelper { public static SpanLayer getLayer(AbstractSpan tracingSpan) { try { - return FieldGetter.getParentFieldValue(tracingSpan, "layer"); + return FieldGetter.get2LevelParentFieldValue(tracingSpan, "layer"); } catch (Exception e) { } @@ -16,7 +16,7 @@ public class SpanHelper { public static int getComponentId(AbstractSpan tracingSpan) { try { - return FieldGetter.getParentFieldValue(tracingSpan, "componentId"); + return FieldGetter.get2LevelParentFieldValue(tracingSpan, "componentId"); } catch (Exception e) { } diff --git a/apm-sniffer/apm-agent/pom.xml b/apm-sniffer/apm-agent/pom.xml index b07bef698..bda4d7137 100644 --- a/apm-sniffer/apm-agent/pom.xml +++ b/apm-sniffer/apm-agent/pom.xml @@ -80,6 +80,21 @@ apm-feign-default-http-9.x-plugin ${project.version} + + org.skywalking + apm-resttemplate-4.3.x-plugin + ${project.version} + + + org.skywalking + apm-spring-concurrent-util-4.x-plugin + ${project.version} + + + org.skywalking + apm-springmvc-annotation-4.x-plugin + ${project.version} + diff --git a/apm-sniffer/apm-sdk-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/pom.xml index 97bf4117a..1b06f19d8 100644 --- a/apm-sniffer/apm-sdk-plugin/pom.xml +++ b/apm-sniffer/apm-sdk-plugin/pom.xml @@ -22,6 +22,7 @@ okhttp-3.x-plugin resin-3.x-plugin resin-4.x-plugin + spring-plugins pom diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/pom.xml new file mode 100644 index 000000000..d399599fb --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/pom.xml @@ -0,0 +1,24 @@ + + + spring-plugins + org.skywalking + 3.2-2017 + + 4.0.0 + + apm-spring-concurrent-util-4.x-plugin + jar + + concurrent-util-4.x-plugin + http://maven.apache.org + + + + org.springframework + spring-core + 4.3.10.RELEASE + provided + + + diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/concurrent/FailureCallbackInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/concurrent/FailureCallbackInterceptor.java new file mode 100644 index 000000000..6166756be --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/concurrent/FailureCallbackInterceptor.java @@ -0,0 +1,43 @@ +package org.skywalking.apm.plugin.spring.concurrent; + +import java.lang.reflect.Method; +import java.net.URI; +import org.skywalking.apm.agent.core.context.ContextManager; +import org.skywalking.apm.agent.core.context.ContextSnapshot; +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 FailureCallbackInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + Object[] cacheValues = (Object[])objInst.getSkyWalkingDynamicField(); + if (cacheValues == null) { + return; + } + + URI uri = (URI)cacheValues[0]; + AbstractSpan span = ContextManager.createLocalSpan("future/failureCallback:" + uri.getPath()); + span.errorOccurred().log((Throwable)allArguments[0]).setComponent(ComponentsDefine.SPRING_REST_TEMPLATE).setLayer(SpanLayer.HTTP); + Tags.URL.set(span, uri.getPath()); + ContextManager.continued((ContextSnapshot)cacheValues[2]); + } + + @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) { + ContextManager.activeSpan().errorOccurred().log(t); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/concurrent/SuccessCallbackInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/concurrent/SuccessCallbackInterceptor.java new file mode 100644 index 000000000..a2cdb5a11 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/concurrent/SuccessCallbackInterceptor.java @@ -0,0 +1,43 @@ +package org.skywalking.apm.plugin.spring.concurrent; + +import java.lang.reflect.Method; +import java.net.URI; +import org.skywalking.apm.agent.core.context.ContextManager; +import org.skywalking.apm.agent.core.context.ContextSnapshot; +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 SuccessCallbackInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + Object[] cacheValues = (Object[])objInst.getSkyWalkingDynamicField(); + if (cacheValues == null) { + return; + } + + URI uri = (URI)cacheValues[0]; + AbstractSpan span = ContextManager.createLocalSpan("future/successCallback:" + uri.getPath()); + span.setComponent(ComponentsDefine.SPRING_REST_TEMPLATE).setLayer(SpanLayer.HTTP); + Tags.URL.set(span, uri.getPath()); + ContextManager.continued((ContextSnapshot)cacheValues[2]); + } + + @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) { + ContextManager.activeSpan().errorOccurred().log(t); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/concurrent/define/FailureCallbackInstrumentation.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/concurrent/define/FailureCallbackInstrumentation.java new file mode 100644 index 000000000..27d79b566 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/concurrent/define/FailureCallbackInstrumentation.java @@ -0,0 +1,56 @@ +package org.skywalking.apm.plugin.spring.concurrent.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +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 org.skywalking.apm.plugin.spring.concurrent.FailureCallbackInterceptor; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static org.skywalking.apm.plugin.spring.concurrent.match.FailedCallbackMatch.failedCallbackMatch; + +/** + * {@link FailureCallbackInstrumentation} enhance the onFailure method that class inherited + * org.springframework.util.concurrent.FailureCallback by {@link FailureCallbackInterceptor}. + * + * @author zhangxin + */ +public class FailureCallbackInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + public static final String FAILURE_CALLBACK_INTERCEPTOR = "org.skywalking.apm.plugin.spring.concurrent.FailureCallbackInterceptor"; + public static final String FAILURE_METHOD_NAME = "onFailure"; + + @Override + protected ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + protected InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[] { + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named(FAILURE_METHOD_NAME); + } + + @Override + public String getMethodsInterceptor() { + return FAILURE_CALLBACK_INTERCEPTOR; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } + + @Override + protected ClassMatch enhanceClass() { + return failedCallbackMatch(); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/concurrent/define/ListenableFutureCallbackInstrumentation.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/concurrent/define/ListenableFutureCallbackInstrumentation.java new file mode 100644 index 000000000..fc1b27111 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/concurrent/define/ListenableFutureCallbackInstrumentation.java @@ -0,0 +1,69 @@ +package org.skywalking.apm.plugin.spring.concurrent.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +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 static net.bytebuddy.matcher.ElementMatchers.named; +import static org.skywalking.apm.plugin.spring.concurrent.match.ListenableFutureCallbackMatch.listenableFutureCallbackMatch; + +/** + * {@link ListenableFutureCallbackInstrumentation} enhance onSuccess method and oonFailure + * that class inherited org.springframework.util.concurrent.ListenableFutureCallback by + * org.skywalking.apm.plugin.spring.concurrent.SuccessCallbackInterceptor and + * org.skywalking.apm.plugin.spring.concurrent.FailureCallbackInterceptor. + * + * @author zhangxin + */ +public class ListenableFutureCallbackInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + @Override + protected ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + protected InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[] { + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named(SuccessCallbackInstrumentation.SUCCESS_METHOD_NAME); + } + + @Override + public String getMethodsInterceptor() { + return SuccessCallbackInstrumentation.SUCCESS_CALLBACK_INTERCEPTOR; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + }, + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return named(FailureCallbackInstrumentation.FAILURE_METHOD_NAME); + } + + @Override + public String getMethodsInterceptor() { + return FailureCallbackInstrumentation.FAILURE_CALLBACK_INTERCEPTOR; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } + + @Override + protected ClassMatch enhanceClass() { + return listenableFutureCallbackMatch(); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/concurrent/define/SuccessCallbackInstrumentation.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/concurrent/define/SuccessCallbackInstrumentation.java new file mode 100644 index 000000000..51c7db24d --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/concurrent/define/SuccessCallbackInstrumentation.java @@ -0,0 +1,50 @@ +package org.skywalking.apm.plugin.spring.concurrent.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +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 static net.bytebuddy.matcher.ElementMatchers.named; +import static org.skywalking.apm.plugin.spring.concurrent.match.SuccessCallbackMatch.successCallbackMatch; + +/** + * {@link SuccessCallbackInstrumentation} enhance the onSuccess method that class inherited + * org.springframework.util.concurrent.SuccessCallback by org.skywalking.apm.plugin.spring.concurrent.SuccessCallbackInterceptor. + * + * @author zhangxin + */ +public class SuccessCallbackInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + public static final String SUCCESS_CALLBACK_INTERCEPTOR = + "org.skywalking.apm.plugin.spring.concurrent.SuccessCallbackInterceptor"; + public static final String SUCCESS_METHOD_NAME = "onSuccess"; + + @Override protected ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override protected InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[] { + new InstanceMethodsInterceptPoint() { + @Override public ElementMatcher getMethodsMatcher() { + return named(SUCCESS_METHOD_NAME); + } + + @Override public String getMethodsInterceptor() { + return SUCCESS_CALLBACK_INTERCEPTOR; + } + + @Override public boolean isOverrideArgs() { + return false; + } + } + }; + } + + @Override protected ClassMatch enhanceClass() { + return successCallbackMatch(); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/concurrent/match/EitherInterfaceMatch.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/concurrent/match/EitherInterfaceMatch.java new file mode 100644 index 000000000..1396c6d20 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/concurrent/match/EitherInterfaceMatch.java @@ -0,0 +1,77 @@ +package org.skywalking.apm.plugin.spring.concurrent.match; + +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.skywalking.apm.agent.core.plugin.match.IndirectMatch; + +import static net.bytebuddy.matcher.ElementMatchers.hasSuperType; +import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.not; + +/** + * {@link EitherInterfaceMatch} match the class inherited {@link #getMatchInterface() } and not inherited {@link + * #getMutexInterface()} + * + * @author zhangxin + */ +public abstract class EitherInterfaceMatch implements IndirectMatch { + + private static final String SPRING_PACKAGE_PREFIX = "org.springframework"; + private static final String OBJECT_CLASS_NAME = "java.lang.Object"; + + protected EitherInterfaceMatch() { + + } + + @Override + public ElementMatcher.Junction buildJunction() { + return not(nameStartsWith(SPRING_PACKAGE_PREFIX)). + and(hasSuperType(named(getMatchInterface()))) + .and(not(hasSuperType(named(getMutexInterface())))); + } + + @Override + public boolean isMatch(TypeDescription typeDescription) { + MatchResult matchResult = new MatchResult(); + for (TypeDescription.Generic generic : typeDescription.getInterfaces()) { + matchHierarchyClazz(generic, matchResult); + } + + matchHierarchyClazz(typeDescription.getSuperClass(), matchResult); + return matchResult.result(); + } + + public abstract String getMatchInterface(); + + public abstract String getMutexInterface(); + + private void matchHierarchyClazz(TypeDescription.Generic clazz, MatchResult matchResult) { + if (clazz.asRawType().getTypeName().equals(getMutexInterface())) { + matchResult.findMutexInterface = true; + return; + } + + if (clazz.asRawType().getTypeName().equals(getMatchInterface())) { + matchResult.findMatchInterface = true; + } + + for (TypeDescription.Generic generic : clazz.getInterfaces()) { + matchHierarchyClazz(generic, matchResult); + } + + TypeDescription.Generic superClazz = clazz.getSuperClass(); + if (superClazz != null && !clazz.getTypeName().equals(OBJECT_CLASS_NAME)) { + matchHierarchyClazz(superClazz, matchResult); + } + } + + private static class MatchResult { + private boolean findMatchInterface = false; + private boolean findMutexInterface = false; + + public boolean result() { + return findMatchInterface && !findMutexInterface; + } + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/concurrent/match/FailedCallbackMatch.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/concurrent/match/FailedCallbackMatch.java new file mode 100644 index 000000000..abcfde249 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/concurrent/match/FailedCallbackMatch.java @@ -0,0 +1,31 @@ +package org.skywalking.apm.plugin.spring.concurrent.match; + +import org.skywalking.apm.agent.core.plugin.match.ClassMatch; + +/** + * {@link FailedCallbackMatch} match the class that inherited org.springframework.util.concurrent.FailureCallback + * and not inherited org.springframework.util.concurrent.SuccessCallback + * + * @author zhangxin + */ +public class FailedCallbackMatch extends EitherInterfaceMatch { + + private static final String MATCH_INTERFACE = "org.springframework.util.concurrent.FailureCallback"; + private static final String MUTEX_INTERFACE = "org.springframework.util.concurrent.SuccessCallback"; + + private FailedCallbackMatch() { + + } + + @Override public String getMatchInterface() { + return MATCH_INTERFACE; + } + + @Override public String getMutexInterface() { + return MUTEX_INTERFACE; + } + + public static ClassMatch failedCallbackMatch() { + return new FailedCallbackMatch(); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/concurrent/match/ListenableFutureCallbackMatch.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/concurrent/match/ListenableFutureCallbackMatch.java new file mode 100644 index 000000000..a18cf6f01 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/concurrent/match/ListenableFutureCallbackMatch.java @@ -0,0 +1,65 @@ +package org.skywalking.apm.plugin.spring.concurrent.match; + +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.skywalking.apm.agent.core.plugin.match.ClassMatch; +import org.skywalking.apm.agent.core.plugin.match.IndirectMatch; + +import static net.bytebuddy.matcher.ElementMatchers.hasSuperType; +import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.not; + +/** + * {@link ListenableFutureCallbackMatch} match the class that inherited org.springframework.util.concurrent.ListenableFutureCallback. + * + * @author zhangxin + */ +public class ListenableFutureCallbackMatch implements IndirectMatch { + + private static final String LISTENABLE_FUTURE_CALLBACK_CLASS_NAME = "org.springframework.util.concurrent.ListenableFutureCallback"; + + private ListenableFutureCallbackMatch() { + + } + + @Override + public ElementMatcher.Junction buildJunction() { + return not(nameStartsWith("org.springframework")). + and(hasSuperType(named(LISTENABLE_FUTURE_CALLBACK_CLASS_NAME))); + } + + @Override + public boolean isMatch(TypeDescription typeDescription) { + boolean isMatch = false; + for (TypeDescription.Generic generic : typeDescription.getInterfaces()) { + isMatch = isMatch || matchExactClass(generic); + } + + return isMatch || matchExactClass(typeDescription.getSuperClass()); + } + + private boolean matchExactClass(TypeDescription.Generic clazz) { + if (clazz.asRawType().getTypeName().equals(LISTENABLE_FUTURE_CALLBACK_CLASS_NAME)) { + return true; + } + + boolean isMatch = false; + for (TypeDescription.Generic generic : clazz.getInterfaces()) { + isMatch = isMatch || matchExactClass(generic); + } + + if (!isMatch) { + TypeDescription.Generic superClazz = clazz.getSuperClass(); + if (superClazz != null && !clazz.getTypeName().equals("java.lang.Object")) { + isMatch = isMatch || matchExactClass(superClazz); + } + } + + return isMatch; + } + + public static ClassMatch listenableFutureCallbackMatch() { + return new ListenableFutureCallbackMatch(); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/concurrent/match/SuccessCallbackMatch.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/concurrent/match/SuccessCallbackMatch.java new file mode 100644 index 000000000..309767198 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/concurrent/match/SuccessCallbackMatch.java @@ -0,0 +1,32 @@ +package org.skywalking.apm.plugin.spring.concurrent.match; + +import org.skywalking.apm.agent.core.plugin.match.ClassMatch; + +/** + * {@link SuccessCallbackMatch} match the class that inherited org.springframework.util.concurrent.SuccessCallback + * and not inherited org.springframework.util.concurrent.FailureCallback + * + * @author zhangxin + */ +public class SuccessCallbackMatch extends EitherInterfaceMatch { + + private static final String MATCH_INTERFACE = "org.springframework.util.concurrent.SuccessCallback"; + private static final String MUTEX_INTERFACE = "org.springframework.util.concurrent.FailureCallback"; + + private SuccessCallbackMatch() { + } + + @Override + public String getMatchInterface() { + return MATCH_INTERFACE; + } + + @Override + public String getMutexInterface() { + return MUTEX_INTERFACE; + } + + public static ClassMatch successCallbackMatch() { + return new SuccessCallbackMatch(); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/src/main/resources/skywalking-plugin.def new file mode 100644 index 000000000..795a7685c --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/concurrent-util-4.x-plugin/src/main/resources/skywalking-plugin.def @@ -0,0 +1,3 @@ +spring-concurrent-util-4.x=org.skywalking.apm.plugin.spring.concurrent.define.FailureCallbackInstrumentation +spring-concurrent-util-4.x=org.skywalking.apm.plugin.spring.concurrent.define.SuccessCallbackInstrumentation +spring-concurrent-util-4.x=org.skywalking.apm.plugin.spring.concurrent.define.ListenableFutureCallbackInstrumentation diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/pom.xml new file mode 100644 index 000000000..bb88c3441 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/pom.xml @@ -0,0 +1,36 @@ + + + spring-plugins + org.skywalking + 3.2-2017 + + 4.0.0 + + apm-springmvc-annotation-4.x-plugin + jar + + mvc-annotation-4.x-plugin + http://maven.apache.org + + + + org.springframework + spring-core + 4.3.10.RELEASE + provided + + + org.springframework + spring-webmvc + 4.3.8.RELEASE + provided + + + javax.servlet + javax.servlet-api + 4.0.0-b01 + provided + + + diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/mvc/ControllerConstructorInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/mvc/ControllerConstructorInterceptor.java new file mode 100644 index 000000000..5f3d39ed0 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/mvc/ControllerConstructorInterceptor.java @@ -0,0 +1,32 @@ +package org.skywalking.apm.plugin.spring.mvc; + +import org.skywalking.apm.agent.core.plugin.interceptor.enhance.EnhancedInstance; +import org.skywalking.apm.agent.core.plugin.interceptor.enhance.InstanceConstructorInterceptor; +import org.springframework.web.bind.annotation.RequestMapping; + +/** + * The ControllerConstructorInterceptor intercepts the Controller's constructor, in order to acquire the + * mapping annotation, if exist. + * + * But, you can see we only use the first mapping value, Why? + * + * Right now, we intercept the controller by annotation as you known, so we CAN'T know which uri patten is actually + * matched. Even we know, that costs a lot. + * + * If we want to resolve that, we must intercept the Spring MVC core codes, that is not a good choice for now. + * + * Comment by @wu-sheng + */ +public class ControllerConstructorInterceptor implements InstanceConstructorInterceptor { + + @Override + public void onConstruct(EnhancedInstance objInst, Object[] allArguments) { + String basePath = ""; + RequestMapping basePathRequestMapping = objInst.getClass().getAnnotation(RequestMapping.class); + if (basePathRequestMapping != null) { + basePath = basePathRequestMapping.value()[0]; + } + PathMappingCache pathMappingCache = new PathMappingCache(basePath); + objInst.setSkyWalkingDynamicField(pathMappingCache); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/mvc/ControllerServiceMethodInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/mvc/ControllerServiceMethodInterceptor.java new file mode 100644 index 000000000..32b4cb42d --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/mvc/ControllerServiceMethodInterceptor.java @@ -0,0 +1,64 @@ +package org.skywalking.apm.plugin.spring.mvc; + +import java.lang.reflect.Method; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import org.skywalking.apm.agent.core.conf.Config; +import org.skywalking.apm.agent.core.context.ContextCarrier; +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; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +/** + * The ControllerServiceMethodInterceptor only use the first mapping value. + * + * @See {@link ControllerConstructorInterceptor} to explain why we are doing this. + */ +public class ControllerServiceMethodInterceptor implements InstanceMethodsAroundInterceptor { + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + PathMappingCache pathMappingCache = (PathMappingCache)objInst.getSkyWalkingDynamicField(); + String requestURL = pathMappingCache.findPathMapping(method); + if (requestURL == null) { + requestURL = method.getAnnotation(RequestMapping.class).value()[0]; + pathMappingCache.addPathMapping(method, requestURL.toString()); + } + + HttpServletRequest request = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getRequest(); + String tracingHeaderValue = request.getHeader(Config.Plugin.Propagation.HEADER_NAME); + ContextCarrier contextCarrier = new ContextCarrier().deserialize(tracingHeaderValue); + AbstractSpan span = ContextManager.createEntrySpan(requestURL, contextCarrier); + Tags.URL.set(span, request.getRequestURL().toString()); + Tags.HTTP.METHOD.set(span, request.getMethod()); + span.setComponent(ComponentsDefine.SPRING_MVC_ANNOTATION); + SpanLayer.asHttp(span); + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + Object ret) throws Throwable { + HttpServletResponse response = ((ServletRequestAttributes)RequestContextHolder.getRequestAttributes()).getResponse(); + + AbstractSpan span = ContextManager.activeSpan(); + if (response.getStatus() >= 400) { + span.errorOccurred(); + Tags.STATUS_CODE.set(span, Integer.toString(response.getStatus())); + } + ContextManager.stopSpan(); + return ret; + } + + @Override public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + ContextManager.activeSpan().errorOccurred().log(t); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/mvc/PathMappingCache.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/mvc/PathMappingCache.java new file mode 100644 index 000000000..591891433 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/mvc/PathMappingCache.java @@ -0,0 +1,28 @@ +package org.skywalking.apm.plugin.spring.mvc; + +import java.lang.reflect.Method; +import java.util.concurrent.ConcurrentHashMap; + +/** + * The PathMappingCache represents a field + * + * + * @author wusheng + */ +public class PathMappingCache { + private String classPath = ""; + + private ConcurrentHashMap methodPathMapping = new ConcurrentHashMap(); + + public PathMappingCache(String classPath) { + this.classPath = classPath; + } + + public String findPathMapping(Method method) { + return methodPathMapping.get(method); + } + + public void addPathMapping(Method method, String methodPath) { + methodPathMapping.put(method, classPath + methodPath); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/mvc/define/AbstractControllerInstrumentation.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/mvc/define/AbstractControllerInstrumentation.java new file mode 100644 index 000000000..96f7715d7 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/mvc/define/AbstractControllerInstrumentation.java @@ -0,0 +1,76 @@ +package org.skywalking.apm.plugin.spring.mvc.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +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 static net.bytebuddy.matcher.ElementMatchers.any; +import static net.bytebuddy.matcher.ElementMatchers.isAnnotatedWith; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static org.skywalking.apm.agent.core.plugin.match.ClassAnnotationMatch.byClassAnnotationMatch; + +/** + * {@link ControllerInstrumentation} enhance all constructor and method annotated with + * org.springframework.web.bind.annotation.RequestMapping that class has + * org.springframework.stereotype.Controller annotation. + * + * org.skywalking.apm.plugin.spring.mvc.ControllerConstructorInterceptor set the controller base path to + * dynamic field before execute constructor. + * + * org.skywalking.apm.plugin.spring.mvc.ControllerServiceMethodInterceptor get the request path from + * dynamic field first, if not found, ControllerServiceMethodInterceptor generate request path that + * combine the path value of current annotation on current method and the base path and set the new path to the dynamic + * filed + * + * @author zhangxin + */ +public abstract class AbstractControllerInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + @Override + protected ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[] { + new ConstructorInterceptPoint() { + @Override + public ElementMatcher getConstructorMatcher() { + return any(); + } + + @Override + public String getConstructorInterceptor() { + return "org.skywalking.apm.plugin.spring.mvc.ControllerConstructorInterceptor"; + } + } + }; + } + + @Override + protected InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[] { + new InstanceMethodsInterceptPoint() { + @Override + public ElementMatcher getMethodsMatcher() { + return isAnnotatedWith(named("org.springframework.web.bind.annotation.RequestMapping")); + } + + @Override + public String getMethodsInterceptor() { + return "org.skywalking.apm.plugin.spring.mvc.ControllerServiceMethodInterceptor"; + } + + @Override + public boolean isOverrideArgs() { + return false; + } + } + }; + } + + @Override + protected ClassMatch enhanceClass() { + return byClassAnnotationMatch(getEnhanceAnnotations()); + } + + protected abstract String[] getEnhanceAnnotations(); +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/mvc/define/ControllerInstrumentation.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/mvc/define/ControllerInstrumentation.java new file mode 100644 index 000000000..abfdf8e29 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/mvc/define/ControllerInstrumentation.java @@ -0,0 +1,10 @@ +package org.skywalking.apm.plugin.spring.mvc.define; + +public class ControllerInstrumentation extends AbstractControllerInstrumentation { + + public static final String ENHANCE_ANNOTATION = "org.springframework.stereotype.Controller"; + + @Override protected String[] getEnhanceAnnotations() { + return new String[] {ENHANCE_ANNOTATION}; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/mvc/define/RestControllerInstrumentation.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/mvc/define/RestControllerInstrumentation.java new file mode 100644 index 000000000..8ce6e59e6 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/mvc/define/RestControllerInstrumentation.java @@ -0,0 +1,10 @@ +package org.skywalking.apm.plugin.spring.mvc.define; + +public class RestControllerInstrumentation extends AbstractControllerInstrumentation { + + public static final String ENHANCE_ANNOTATION = "org.springframework.web.bind.annotation.RestController"; + + @Override protected String[] getEnhanceAnnotations() { + return new String[] {ENHANCE_ANNOTATION}; + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/src/main/resources/skywalking-plugin.def new file mode 100644 index 000000000..92d3695ba --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/mvc-annotation-4.x-plugin/src/main/resources/skywalking-plugin.def @@ -0,0 +1,2 @@ +spring-mvc-annotation-4.x=org.skywalking.apm.plugin.spring.mvc.define.ControllerInstrumentation +spring-mvc-annotation-4.x=org.skywalking.apm.plugin.spring.mvc.define.RestControllerInstrumentation \ No newline at end of file diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/pom.xml b/apm-sniffer/apm-sdk-plugin/spring-plugins/pom.xml new file mode 100644 index 000000000..0a91d8890 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/pom.xml @@ -0,0 +1,27 @@ + + + 4.0.0 + + + org.skywalking + apm-sdk-plugin + 3.2-2017 + + + spring-plugins + + concurrent-util-4.x-plugin + resttemplate-4.x-plugin + mvc-annotation-4.x-plugin + + pom + + apm-sdk-plugin + http://maven.apache.org + + + UTF-8 + + + diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/pom.xml b/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/pom.xml new file mode 100644 index 000000000..874a25df4 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/pom.xml @@ -0,0 +1,24 @@ + + + spring-plugins + org.skywalking + 3.2-2017 + + 4.0.0 + + apm-resttemplate-4.3.x-plugin + jar + + resttemplate-4.3.x-plugin + http://maven.apache.org + + + + org.springframework + spring-web + 4.3.10.RELEASE + provided + + + diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/resttemplate/async/FutureGetInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/resttemplate/async/FutureGetInterceptor.java new file mode 100644 index 000000000..218113022 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/resttemplate/async/FutureGetInterceptor.java @@ -0,0 +1,32 @@ +package org.skywalking.apm.plugin.spring.resttemplate.async; + +import java.lang.reflect.Method; +import java.net.URI; +import org.skywalking.apm.agent.core.context.ContextManager; +import org.skywalking.apm.agent.core.context.trace.AbstractSpan; +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; + +public class FutureGetInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + Object[] cacheValues = (Object[])objInst.getSkyWalkingDynamicField(); + ContextManager.createLocalSpan("future/get:" + ((URI)cacheValues[0]).getPath()); + } + + @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().log(t); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/resttemplate/async/ResponseCallBackInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/resttemplate/async/ResponseCallBackInterceptor.java new file mode 100644 index 000000000..421312ebe --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/resttemplate/async/ResponseCallBackInterceptor.java @@ -0,0 +1,32 @@ +package org.skywalking.apm.plugin.spring.resttemplate.async; + +import java.lang.reflect.Method; +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; + +public class ResponseCallBackInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + EnhancedInstance successCallBak = (EnhancedInstance)allArguments[0]; + successCallBak.setSkyWalkingDynamicField(objInst.getSkyWalkingDynamicField()); + + if (allArguments.length == 2) { + EnhancedInstance failedCallBack = (EnhancedInstance)allArguments[1]; + failedCallBack.setSkyWalkingDynamicField(objInst.getSkyWalkingDynamicField()); + } + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + Object ret) throws Throwable { + return ret; + } + + @Override public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/resttemplate/async/RestExecuteInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/resttemplate/async/RestExecuteInterceptor.java new file mode 100644 index 000000000..4ee3e2581 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/resttemplate/async/RestExecuteInterceptor.java @@ -0,0 +1,53 @@ +package org.skywalking.apm.plugin.spring.resttemplate.async; + +import java.lang.reflect.Method; +import java.net.URI; +import org.skywalking.apm.agent.core.context.ContextCarrier; +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; +import org.springframework.http.HttpMethod; + +public class RestExecuteInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + final URI requestURL = (URI)allArguments[0]; + final HttpMethod httpMethod = (HttpMethod)allArguments[1]; + final ContextCarrier contextCarrier = new ContextCarrier(); + String remotePeer = requestURL.getHost() + ":" + requestURL.getPort(); + AbstractSpan span = ContextManager.createExitSpan(requestURL.getPath(), contextCarrier, remotePeer); + + span.setComponent(ComponentsDefine.SPRING_REST_TEMPLATE); + Tags.URL.set(span, requestURL.getScheme() + "://" + requestURL.getHost() + ":" + requestURL.getPort() + requestURL.getPath()); + Tags.HTTP.METHOD.set(span, httpMethod.toString()); + SpanLayer.asHttp(span); + Object[] cacheValues = new Object[3]; + cacheValues[0] = requestURL; + cacheValues[1] = contextCarrier.serialize(); + objInst.setSkyWalkingDynamicField(cacheValues); + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + Object ret) throws Throwable { + Object[] cacheValues = (Object[])objInst.getSkyWalkingDynamicField(); + cacheValues[2] = ContextManager.capture(); + if (ret != null) { + ((EnhancedInstance)ret).setSkyWalkingDynamicField(cacheValues); + } + ContextManager.stopSpan(); + return ret; + } + + @Override public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + ContextManager.activeSpan().errorOccurred().log(t); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/resttemplate/async/define/ResponseExtractorFutureInstrumentation.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/resttemplate/async/define/ResponseExtractorFutureInstrumentation.java new file mode 100644 index 000000000..858fe5a7f --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/resttemplate/async/define/ResponseExtractorFutureInstrumentation.java @@ -0,0 +1,77 @@ +package org.skywalking.apm.plugin.spring.resttemplate.async.define; + +import java.net.URI; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.skywalking.apm.agent.core.context.ContextSnapshot; +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 org.skywalking.apm.plugin.spring.resttemplate.async.ResponseCallBackInterceptor; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static org.skywalking.apm.agent.core.plugin.match.NameMatch.byName; + +/** + * {@link ResponseExtractorFutureInstrumentation} enhance the addCallback method and get method of + * org.springframework.web.client.AsyncRestTemplate$ResponseExtractorFuture by + * org.skywalking.apm.plugin.spring.resttemplate.async.ResponseCallBackInterceptor and + * org.skywalking.apm.plugin.spring.resttemplate.async.FutureGetInterceptor. + * + * {@link ResponseCallBackInterceptor} set the {@link URI} and {@link ContextSnapshot} to inherited + * org.springframework.util.concurrent.SuccessCallback and org.springframework.util.concurrent.FailureCallback + * + * @author zhangxin + */ +public class ResponseExtractorFutureInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ADD_CALLBACK_METHOD_NAME = "addCallback"; + private static final String ADD_CALLBACK_INTERCEPTOR = "org.skywalking.apm.plugin.spring.resttemplate.async.ResponseCallBackInterceptor"; + private static final String ENHANCE_CLASS = "org.springframework.web.client.AsyncRestTemplate$ResponseExtractorFuture"; + private static final String GET_METHOD_INTERCEPTOR = "org.skywalking.apm.plugin.spring.resttemplate.async.FutureGetInterceptor"; + private static final String GET_METHOD_NAME = "get"; + + @Override + protected ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + + } + + @Override + protected InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[] { + new InstanceMethodsInterceptPoint() { + @Override public ElementMatcher getMethodsMatcher() { + return named(ADD_CALLBACK_METHOD_NAME); + } + + @Override public String getMethodsInterceptor() { + return ADD_CALLBACK_INTERCEPTOR; + } + + @Override public boolean isOverrideArgs() { + return false; + } + }, + new InstanceMethodsInterceptPoint() { + @Override public ElementMatcher getMethodsMatcher() { + return named(GET_METHOD_NAME); + } + + @Override public String getMethodsInterceptor() { + return GET_METHOD_INTERCEPTOR; + } + + @Override public boolean isOverrideArgs() { + return false; + } + } + }; + } + + @Override + protected ClassMatch enhanceClass() { + return byName(ENHANCE_CLASS); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/resttemplate/async/define/RestTemplateInstrumentation.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/resttemplate/async/define/RestTemplateInstrumentation.java new file mode 100644 index 000000000..1cb6a20b0 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/resttemplate/async/define/RestTemplateInstrumentation.java @@ -0,0 +1,75 @@ +package org.skywalking.apm.plugin.spring.resttemplate.async.define; + +import java.net.URI; +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.skywalking.apm.agent.core.context.ContextSnapshot; +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 static net.bytebuddy.matcher.ElementMatchers.named; +import static org.skywalking.apm.agent.core.plugin.match.NameMatch.byName; + +/** + * {@link RestTemplateInstrumentation} enhance the doExecute method and createAsyncRequest + * method of org.springframework.web.client.AsyncRestTemplate by org.skywalking.apm.plugin.spring.resttemplate.async.RestExecuteInterceptor + * and org.springframework.http.client.RestRequestInterceptor. + * + * org.springframework.http.client.RestRequestInterceptor set {@link URI} and {@link ContextSnapshot} to + * org.springframework.web.client.AsyncRestTemplate$ResponseExtractorFuture for propagate trace context + * after execute doExecute . + * + * @author zhangxin + */ +public class RestTemplateInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "org.springframework.web.client.AsyncRestTemplate"; + private static final String DO_EXECUTE_METHOD_NAME = "doExecute"; + private static final String DO_EXECUTE_INTERCEPTOR = "org.skywalking.apm.plugin.spring.resttemplate.async.RestExecuteInterceptor"; + private static final String CREATE_REQUEST_METHOD_NAME = "createAsyncRequest"; + private static final String CREATE_REQUEST_INTERCEPTOR = "org.springframework.http.client.RestRequestInterceptor"; + + @Override + protected ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + protected InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[] { + new InstanceMethodsInterceptPoint() { + @Override public ElementMatcher getMethodsMatcher() { + return named(DO_EXECUTE_METHOD_NAME); + } + + @Override public String getMethodsInterceptor() { + return DO_EXECUTE_INTERCEPTOR; + } + + @Override public boolean isOverrideArgs() { + return false; + } + }, + new InstanceMethodsInterceptPoint() { + @Override public ElementMatcher getMethodsMatcher() { + return named(CREATE_REQUEST_METHOD_NAME); + } + + @Override public String getMethodsInterceptor() { + return CREATE_REQUEST_INTERCEPTOR; + } + + @Override public boolean isOverrideArgs() { + return false; + } + } + }; + } + + @Override + protected ClassMatch enhanceClass() { + return byName(ENHANCE_CLASS); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/resttemplate/sync/RestExecuteInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/resttemplate/sync/RestExecuteInterceptor.java new file mode 100644 index 000000000..a0764191b --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/resttemplate/sync/RestExecuteInterceptor.java @@ -0,0 +1,46 @@ +package org.skywalking.apm.plugin.spring.resttemplate.sync; + +import java.lang.reflect.Method; +import java.net.URI; +import org.skywalking.apm.agent.core.context.ContextCarrier; +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; +import org.springframework.http.HttpMethod; + +public class RestExecuteInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + final URI requestURL = (URI)allArguments[0]; + final HttpMethod httpMethod = (HttpMethod)allArguments[1]; + final ContextCarrier contextCarrier = new ContextCarrier(); + String remotePeer = requestURL.getHost() + ":" + requestURL.getPort(); + AbstractSpan span = ContextManager.createExitSpan(requestURL.getPath(), contextCarrier, remotePeer); + + span.setComponent(ComponentsDefine.SPRING_REST_TEMPLATE); + Tags.URL.set(span, requestURL.getScheme() + "://" + requestURL.getHost() + ":" + requestURL.getPort() + requestURL.getPath()); + Tags.HTTP.METHOD.set(span, httpMethod.toString()); + SpanLayer.asHttp(span); + + objInst.setSkyWalkingDynamicField(contextCarrier.serialize()); + } + + @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) { + ContextManager.activeSpan().errorOccurred().log(t); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/resttemplate/sync/RestRequestInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/resttemplate/sync/RestRequestInterceptor.java new file mode 100644 index 000000000..23e062c5b --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/resttemplate/sync/RestRequestInterceptor.java @@ -0,0 +1,34 @@ +package org.skywalking.apm.plugin.spring.resttemplate.sync; + +import java.lang.reflect.Method; +import org.skywalking.apm.agent.core.conf.Config; +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.springframework.http.client.AbstractClientHttpRequest; +import org.springframework.http.client.ClientHttpRequest; + +public class RestRequestInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + Object ret) throws Throwable { + ClientHttpRequest clientHttpRequest = (ClientHttpRequest)ret; + if (clientHttpRequest instanceof AbstractClientHttpRequest) { + AbstractClientHttpRequest httpRequest = (AbstractClientHttpRequest)clientHttpRequest; + httpRequest.getHeaders().set(Config.Plugin.Propagation.HEADER_NAME, String.valueOf(objInst.getSkyWalkingDynamicField())); + } + return ret; + } + + @Override public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/resttemplate/sync/RestResponseInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/resttemplate/sync/RestResponseInterceptor.java new file mode 100644 index 000000000..80e554f52 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/resttemplate/sync/RestResponseInterceptor.java @@ -0,0 +1,38 @@ +package org.skywalking.apm.plugin.spring.resttemplate.sync; + +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.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.springframework.http.client.ClientHttpResponse; + +public class RestResponseInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + Object ret) throws Throwable { + + ClientHttpResponse response = (ClientHttpResponse)allArguments[2]; + int statusCode = response.getStatusCode().value(); + AbstractSpan span = ContextManager.activeSpan(); + if (statusCode >= 400) { + span.errorOccurred(); + Tags.STATUS_CODE.set(span, Integer.toString(statusCode)); + } + return ret; + } + + @Override public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + ContextManager.activeSpan().errorOccurred().log(t); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/resttemplate/sync/define/RestTemplateInstrumentation.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/resttemplate/sync/define/RestTemplateInstrumentation.java new file mode 100644 index 000000000..9fb529bf1 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/src/main/java/org/skywalking/apm/plugin/spring/resttemplate/sync/define/RestTemplateInstrumentation.java @@ -0,0 +1,89 @@ +package org.skywalking.apm.plugin.spring.resttemplate.sync.define; + +import net.bytebuddy.description.method.MethodDescription; +import net.bytebuddy.matcher.ElementMatcher; +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 static net.bytebuddy.matcher.ElementMatchers.named; +import static org.skywalking.apm.agent.core.plugin.match.NameMatch.byName; + +/** + * {@link RestTemplateInstrumentation} enhance the doExecute method,handleResponse method and + * handleResponse method of org.springframework.web.client.RestTemplate by + * org.skywalking.apm.plugin.spring.resttemplate.sync.RestExecuteInterceptor, + * org.skywalking.apm.plugin.spring.resttemplate.sync.RestResponseInterceptor and + * org.skywalking.apm.plugin.spring.resttemplate.sync.RestRequestInterceptor. + * + * org.skywalking.apm.plugin.spring.resttemplate.sync.RestResponseInterceptor set context to header for + * propagate trace context after execute createRequest. + * + * @author zhangxin + */ +public class RestTemplateInstrumentation extends ClassInstanceMethodsEnhancePluginDefine { + + private static final String ENHANCE_CLASS = "org.springframework.web.client.RestTemplate"; + private static final String DO_EXECUTE_METHOD_NAME = "doExecute"; + private static final String DO_EXECUTE_INTERCEPTOR = "org.skywalking.apm.plugin.spring.resttemplate.sync.RestExecuteInterceptor"; + private static final String HANDLE_REQUEST_METHOD_NAME = "handleResponse"; + private static final String HAND_REQUEST_INTERCEPTOR = "org.skywalking.apm.plugin.spring.resttemplate.sync.RestResponseInterceptor"; + private static final String CREATE_REQUEST_METHOD_NAME = "createRequest"; + private static final String CREATE_REQUEST_INTERCEPTOR = "org.skywalking.apm.plugin.spring.resttemplate.sync.RestRequestInterceptor"; + + @Override + protected ConstructorInterceptPoint[] getConstructorsInterceptPoints() { + return new ConstructorInterceptPoint[0]; + } + + @Override + protected InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() { + return new InstanceMethodsInterceptPoint[] { + new InstanceMethodsInterceptPoint() { + @Override public ElementMatcher getMethodsMatcher() { + return named(DO_EXECUTE_METHOD_NAME); + } + + @Override public String getMethodsInterceptor() { + return DO_EXECUTE_INTERCEPTOR; + } + + @Override public boolean isOverrideArgs() { + return false; + } + }, + new InstanceMethodsInterceptPoint() { + @Override public ElementMatcher getMethodsMatcher() { + return named(HANDLE_REQUEST_METHOD_NAME); + } + + @Override public String getMethodsInterceptor() { + return HAND_REQUEST_INTERCEPTOR; + } + + @Override public boolean isOverrideArgs() { + return false; + } + }, + new InstanceMethodsInterceptPoint() { + @Override public ElementMatcher getMethodsMatcher() { + return named(CREATE_REQUEST_METHOD_NAME); + } + + @Override public String getMethodsInterceptor() { + return CREATE_REQUEST_INTERCEPTOR; + } + + @Override public boolean isOverrideArgs() { + return false; + } + } + }; + } + + @Override + protected ClassMatch enhanceClass() { + return byName(ENHANCE_CLASS); + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/src/main/java/org/springframework/http/client/RestRequestInterceptor.java b/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/src/main/java/org/springframework/http/client/RestRequestInterceptor.java new file mode 100644 index 000000000..f8bde0b91 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/src/main/java/org/springframework/http/client/RestRequestInterceptor.java @@ -0,0 +1,31 @@ +package org.springframework.http.client; + +import java.lang.reflect.Method; +import org.skywalking.apm.agent.core.conf.Config; +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; + +public class RestRequestInterceptor implements InstanceMethodsAroundInterceptor { + + @Override + public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + MethodInterceptResult result) throws Throwable { + + } + + @Override + public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class[] argumentsTypes, + Object ret) throws Throwable { + AbstractAsyncClientHttpRequest clientHttpRequest = (AbstractAsyncClientHttpRequest)ret; + if (ret != null) { + clientHttpRequest.getHeaders().set(Config.Plugin.Propagation.HEADER_NAME, String.valueOf(((Object[])objInst.getSkyWalkingDynamicField())[1])); + } + return ret; + } + + @Override public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments, + Class[] argumentsTypes, Throwable t) { + + } +} diff --git a/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/src/main/resources/skywalking-plugin.def b/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/src/main/resources/skywalking-plugin.def new file mode 100644 index 000000000..aa695c514 --- /dev/null +++ b/apm-sniffer/apm-sdk-plugin/spring-plugins/resttemplate-4.x-plugin/src/main/resources/skywalking-plugin.def @@ -0,0 +1,3 @@ +spring-resttemplate-4.x=org.skywalking.apm.plugin.spring.resttemplate.async.define.RestTemplateInstrumentation +spring-resttemplate-4.x=org.skywalking.apm.plugin.spring.resttemplate.async.define.ResponseExtractorFutureInstrumentation +spring-resttemplate-4.x=org.skywalking.apm.plugin.spring.resttemplate.sync.define.RestTemplateInstrumentation diff --git a/apm-sniffer/apm-test-tools/src/main/java/org/skywalking/apm/agent/test/helper/FieldGetter.java b/apm-sniffer/apm-test-tools/src/main/java/org/skywalking/apm/agent/test/helper/FieldGetter.java index 1710b93ca..b2bb6f464 100644 --- a/apm-sniffer/apm-test-tools/src/main/java/org/skywalking/apm/agent/test/helper/FieldGetter.java +++ b/apm-sniffer/apm-test-tools/src/main/java/org/skywalking/apm/agent/test/helper/FieldGetter.java @@ -16,4 +16,12 @@ public class FieldGetter { field.setAccessible(true); return (T)field.get(instance); } + + + public static T get2LevelParentFieldValue(Object instance, + String fieldName) throws IllegalAccessException, NoSuchFieldException { + Field field = instance.getClass().getSuperclass().getSuperclass().getDeclaredField(fieldName); + field.setAccessible(true); + return (T)field.get(instance); + } } diff --git a/apm-sniffer/apm-test-tools/src/main/java/org/skywalking/apm/agent/test/helper/SpanHelper.java b/apm-sniffer/apm-test-tools/src/main/java/org/skywalking/apm/agent/test/helper/SpanHelper.java index 3620fa741..0b752e947 100644 --- a/apm-sniffer/apm-test-tools/src/main/java/org/skywalking/apm/agent/test/helper/SpanHelper.java +++ b/apm-sniffer/apm-test-tools/src/main/java/org/skywalking/apm/agent/test/helper/SpanHelper.java @@ -10,8 +10,13 @@ import org.skywalking.apm.agent.core.context.util.KeyValuePair; public class SpanHelper { public static int getParentSpanId(AbstractSpan tracingSpan) { try { - return FieldGetter.getParentFieldValue(tracingSpan, "parentSpanId"); + return FieldGetter.get2LevelParentFieldValue(tracingSpan, "parentSpanId"); } catch (Exception e) { + try { + return FieldGetter.getParentFieldValue(tracingSpan, "parentSpanId"); + } catch (Exception e1) { + + } } return -9999; @@ -19,8 +24,13 @@ public class SpanHelper { public static List getLogs(AbstractSpan tracingSpan) { try { - return FieldGetter.getParentFieldValue(tracingSpan, "logs"); + return FieldGetter.get2LevelParentFieldValue(tracingSpan, "logs"); } catch (Exception e) { + try { + return FieldGetter.getParentFieldValue(tracingSpan, "logs"); + } catch (Exception e1) { + + } } return Collections.emptyList(); @@ -28,8 +38,13 @@ public class SpanHelper { public static List getTags(AbstractSpan tracingSpan) { try { - return FieldGetter.getParentFieldValue(tracingSpan, "tags"); + return FieldGetter.get2LevelParentFieldValue(tracingSpan, "tags"); } catch (Exception e) { + try { + return FieldGetter.getParentFieldValue(tracingSpan, "tags"); + } catch (Exception e1) { + + } } return Collections.emptyList(); @@ -37,8 +52,13 @@ public class SpanHelper { public static SpanLayer getLayer(AbstractSpan tracingSpan) { try { - return FieldGetter.getParentFieldValue(tracingSpan, "layer"); + return FieldGetter.get2LevelParentFieldValue(tracingSpan, "layer"); } catch (Exception e) { + try { + return FieldGetter.getParentFieldValue(tracingSpan, "layer"); + } catch (Exception e1) { + + } } return null; @@ -46,8 +66,13 @@ public class SpanHelper { public static String getComponentName(AbstractSpan tracingSpan) { try { - return FieldGetter.getParentFieldValue(tracingSpan, "componentName"); + return FieldGetter.get2LevelParentFieldValue(tracingSpan, "componentName"); } catch (Exception e) { + try { + return FieldGetter.getParentFieldValue(tracingSpan, "componentName"); + } catch (Exception e1) { + + } } return null; @@ -55,8 +80,13 @@ public class SpanHelper { public static int getComponentId(AbstractSpan tracingSpan) { try { - return FieldGetter.getParentFieldValue(tracingSpan, "componentId"); + return FieldGetter.get2LevelParentFieldValue(tracingSpan, "componentId"); } catch (Exception e) { + try { + return FieldGetter.getParentFieldValue(tracingSpan, "componentId"); + } catch (Exception e1) { + + } } return -1; @@ -64,8 +94,13 @@ public class SpanHelper { public static boolean getErrorOccurred(AbstractSpan tracingSpan) { try { - return FieldGetter.getParentFieldValue(tracingSpan, "errorOccurred"); + return FieldGetter.get2LevelParentFieldValue(tracingSpan, "errorOccurred"); } catch (Exception e) { + try { + return FieldGetter.getParentFieldValue(tracingSpan, "errorOccurred"); + } catch (Exception e1) { + + } } return false;