Modify the Tomcat plugin using the new API
This commit is contained in:
parent
a86266b8d5
commit
82ce2ad225
|
|
@ -12,9 +12,8 @@ import net.bytebuddy.matcher.ElementMatcher;
|
|||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
|
||||
/**
|
||||
* {@link DubboInstrumentation} present that skywalking use class {@link DubboInterceptor} to
|
||||
* enhance class {@link com.alibaba.dubbo.monitor.support.MonitorFilter#invoke(Invoker, Invocation)}
|
||||
* for support trace of the dubbo framework.
|
||||
* {@link DubboInstrumentation} presents that skywalking use class {@link DubboInterceptor} to
|
||||
* intercept {@link com.alibaba.dubbo.monitor.support.MonitorFilter#invoke(Invoker, Invocation)}.
|
||||
*
|
||||
* @author zhangxin
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -33,6 +33,12 @@
|
|||
<artifactId>skywalking-api</artifactId>
|
||||
<version>3.0-2017</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.a.eye</groupId>
|
||||
<artifactId>skywalking-sniffer-mock</artifactId>
|
||||
<version>3.0-2017</version>
|
||||
<scope>test</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
|
|
|
|||
|
|
@ -1,70 +0,0 @@
|
|||
package com.a.eye.skywalking.api.plugin.tomcat78x;
|
||||
|
||||
import com.a.eye.skywalking.api.Tracing;
|
||||
import com.a.eye.skywalking.invoke.monitor.RPCServerInvokeMonitor;
|
||||
import com.a.eye.skywalking.model.ContextData;
|
||||
import com.a.eye.skywalking.model.Identification;
|
||||
import com.a.eye.skywalking.api.plugin.interceptor.EnhancedClassInstanceContext;
|
||||
import com.a.eye.skywalking.plugin.interceptor.enhance.ConstructorInvokeContext;
|
||||
import com.a.eye.skywalking.plugin.interceptor.enhance.InstanceMethodInvokeContext;
|
||||
import com.a.eye.skywalking.api.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
|
||||
import com.a.eye.skywalking.api.plugin.interceptor.enhance.MethodInterceptResult;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
public class TomcatPluginInterceptor implements InstanceMethodsAroundInterceptor {
|
||||
private final String secondKey = "ContextData";
|
||||
private String tracingName = DEFAULT_TRACE_NAME;
|
||||
private static final String DEFAULT_TRACE_NAME = "SkyWalking-TRACING-NAME";
|
||||
private static final String TRACE_ID_HEADER_NAME = "SW-TraceId";
|
||||
|
||||
@Override
|
||||
public void beforeMethod(EnhancedClassInstanceContext context, InstanceMethodInvokeContext interceptorContext, MethodInterceptResult result) {
|
||||
Object[] args = interceptorContext.allArguments();
|
||||
HttpServletRequest requests = (HttpServletRequest) args[0];
|
||||
String tracingHeaderValue = requests.getHeader(tracingName);
|
||||
ContextData contextData = null;
|
||||
if (tracingHeaderValue != null) {
|
||||
String contextDataStr = null;
|
||||
int index = tracingHeaderValue.indexOf("=");
|
||||
if (index > 0) {
|
||||
String key = tracingHeaderValue.substring(0, index);
|
||||
if (secondKey.equals(key)) {
|
||||
contextDataStr = tracingHeaderValue.substring(index + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (contextDataStr != null && contextDataStr.length() > 0) {
|
||||
contextData = new ContextData(contextDataStr);
|
||||
}
|
||||
}
|
||||
RPCServerInvokeMonitor rpcServerInvokeMonitor = new RPCServerInvokeMonitor();
|
||||
rpcServerInvokeMonitor.beforeInvoke(contextData, generateIdentification(requests));
|
||||
}
|
||||
|
||||
|
||||
private Identification generateIdentification(HttpServletRequest request) {
|
||||
return Identification.newBuilder()
|
||||
.viewPoint(request.getRequestURL().toString())
|
||||
.spanType(WebBuriedPointType.INSTANCE)
|
||||
.build();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public Object afterMethod(EnhancedClassInstanceContext context, InstanceMethodInvokeContext interceptorContext, Object ret) {
|
||||
Object[] args = interceptorContext.allArguments();
|
||||
HttpServletResponse httpServletResponse = (HttpServletResponse) args[1];
|
||||
httpServletResponse.addHeader(TRACE_ID_HEADER_NAME, Tracing.getTraceId());
|
||||
new RPCServerInvokeMonitor().afterInvoke();
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMethodException(Throwable t, EnhancedClassInstanceContext context,
|
||||
InstanceMethodInvokeContext interceptorContext) {
|
||||
new RPCServerInvokeMonitor().occurException(t);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,18 +0,0 @@
|
|||
package com.a.eye.skywalking.api.plugin.tomcat78x;
|
||||
|
||||
import com.a.eye.skywalking.api.IBuriedPointType;
|
||||
|
||||
public enum WebBuriedPointType implements IBuriedPointType {
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public String getTypeName() {
|
||||
return "W";
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallType getCallType() {
|
||||
return CallType.SYNC;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -1,36 +0,0 @@
|
|||
package com.a.eye.skywalking.api.plugin.tomcat78x.define;
|
||||
|
||||
import com.a.eye.skywalking.plugin.interceptor.ConstructorInterceptPoint;
|
||||
import com.a.eye.skywalking.api.plugin.interceptor.InstanceMethodsInterceptPoint;
|
||||
import com.a.eye.skywalking.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
|
||||
public class TomcatPluginDefine extends ClassInstanceMethodsEnhancePluginDefine {
|
||||
@Override
|
||||
protected String enhanceClassName() {
|
||||
return "org.apache.catalina.core.StandardEngineValve";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
|
||||
return new InstanceMethodsInterceptPoint[]{new InstanceMethodsInterceptPoint() {
|
||||
@Override
|
||||
public ElementMatcher<MethodDescription> getMethodsMatcher() {
|
||||
return named("invoke");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMethodsInterceptor() {
|
||||
return "com.a.eye.skywalking.plugin.tomcat78x.TomcatPluginInterceptor";
|
||||
}
|
||||
}};
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,68 @@
|
|||
package com.a.eye.skywalking.plugin.tomcat78x;
|
||||
|
||||
import com.a.eye.skywalking.api.context.ContextCarrier;
|
||||
import com.a.eye.skywalking.api.context.ContextManager;
|
||||
import com.a.eye.skywalking.api.plugin.interceptor.EnhancedClassInstanceContext;
|
||||
import com.a.eye.skywalking.api.plugin.interceptor.enhance.InstanceMethodInvokeContext;
|
||||
import com.a.eye.skywalking.api.plugin.interceptor.enhance.InstanceMethodsAroundInterceptor;
|
||||
import com.a.eye.skywalking.api.plugin.interceptor.enhance.MethodInterceptResult;
|
||||
import com.a.eye.skywalking.api.util.StringUtil;
|
||||
import com.a.eye.skywalking.trace.Span;
|
||||
import com.a.eye.skywalking.trace.tag.Tags;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* {@link TomcatInterceptor} fetch the serialized context data by use {@link HttpServletRequest#getHeader(String)}.
|
||||
* The {@link com.a.eye.skywalking.trace.TraceSegment#primaryRef} of current trace segment will reference to the trace segment id
|
||||
* of the previous level if the serialized context is not null.
|
||||
*/
|
||||
public class TomcatInterceptor implements InstanceMethodsAroundInterceptor {
|
||||
public static final String HEADER_NAME_OF_CONTEXT_DATA = "SKYWALKING_CONTEXT_DATA";
|
||||
public static final String TOMCAT_COMPONENT = "Tomcat";
|
||||
|
||||
/**
|
||||
* The {@link com.a.eye.skywalking.trace.TraceSegment#primaryRef} of current trace segment will reference to the trace segment id
|
||||
* of the previous level if the serialized context is not null.
|
||||
*
|
||||
* @param context instance context, a class instance only has one {@link EnhancedClassInstanceContext} instance.
|
||||
* @param interceptorContext method context, includes class name, method name, etc.
|
||||
* @param result change this result, if you want to truncate the method.
|
||||
*/
|
||||
@Override
|
||||
public void beforeMethod(EnhancedClassInstanceContext context, InstanceMethodInvokeContext interceptorContext, MethodInterceptResult result) {
|
||||
Object[] args = interceptorContext.allArguments();
|
||||
HttpServletRequest request = (HttpServletRequest) args[0];
|
||||
|
||||
Span span = ContextManager.INSTANCE.createSpan(request.getRequestURI());
|
||||
Tags.COMPONENT.set(span, TOMCAT_COMPONENT);
|
||||
Tags.URL.set(span, request.getRequestURL().toString());
|
||||
Tags.SPAN_LAYER.asHttp(span);
|
||||
|
||||
String tracingHeaderValue = request.getHeader(HEADER_NAME_OF_CONTEXT_DATA);
|
||||
if (!StringUtil.isEmpty(tracingHeaderValue)) {
|
||||
ContextManager.INSTANCE.extract(new ContextCarrier().deserialize(tracingHeaderValue));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object afterMethod(EnhancedClassInstanceContext context, InstanceMethodInvokeContext interceptorContext, Object ret) {
|
||||
HttpServletResponse response = (HttpServletResponse) interceptorContext.allArguments()[1];
|
||||
|
||||
Span span = ContextManager.INSTANCE.activeSpan();
|
||||
Tags.STATUS_CODE.set(span, response.getStatus());
|
||||
ContextManager.INSTANCE.stopSpan();
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handleMethodException(Throwable t, EnhancedClassInstanceContext context,
|
||||
InstanceMethodInvokeContext interceptorContext) {
|
||||
Span span = ContextManager.INSTANCE.activeSpan();
|
||||
span.log(t);
|
||||
Tags.ERROR.set(span, true);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,52 @@
|
|||
package com.a.eye.skywalking.plugin.tomcat78x.define;
|
||||
|
||||
import com.a.eye.skywalking.api.plugin.interceptor.ConstructorInterceptPoint;
|
||||
import com.a.eye.skywalking.api.plugin.interceptor.InstanceMethodsInterceptPoint;
|
||||
import com.a.eye.skywalking.api.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
|
||||
import com.a.eye.skywalking.plugin.tomcat78x.TomcatInterceptor;
|
||||
|
||||
import net.bytebuddy.description.method.MethodDescription;
|
||||
import net.bytebuddy.matcher.ElementMatcher;
|
||||
|
||||
import org.apache.catalina.connector.Request;
|
||||
import org.apache.catalina.connector.Response;
|
||||
|
||||
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||
|
||||
/**
|
||||
* {@link TomcatInstrumentation} presents that skywalking use class {@link TomcatInterceptor} to
|
||||
* intercept {@link org.apache.catalina.core.StandardEngineValve#invoke(Request, Response)}.
|
||||
*
|
||||
* @author zhangxin
|
||||
*/
|
||||
public class TomcatInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
|
||||
|
||||
private static final String ENHANCE_CLASS = "org.apache.catalina.core.StandardEngineValve";
|
||||
|
||||
private static final String INTERCEPT_CLASS = "com.a.eye.skywalking.plugin.tomcat78x.TomcatInterceptor";
|
||||
|
||||
@Override
|
||||
protected String enhanceClassName() {
|
||||
return ENHANCE_CLASS;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
|
||||
return new InstanceMethodsInterceptPoint[]{new InstanceMethodsInterceptPoint() {
|
||||
@Override
|
||||
public ElementMatcher<MethodDescription> getMethodsMatcher() {
|
||||
return named("invoke");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getMethodsInterceptor() {
|
||||
return INTERCEPT_CLASS;
|
||||
}
|
||||
}};
|
||||
}
|
||||
}
|
||||
|
|
@ -1 +1 @@
|
|||
com.a.eye.skywalking.plugin.tomcat78x.define.TomcatPluginDefine
|
||||
com.a.eye.skywalking.plugin.tomcat78x.define.TomcatInstrumentation
|
||||
|
|
|
|||
|
|
@ -0,0 +1,140 @@
|
|||
package com.a.eye.skywalking.plugin.tomcat78x;
|
||||
|
||||
import com.a.eye.skywalking.api.context.TracerContext;
|
||||
import com.a.eye.skywalking.api.plugin.interceptor.EnhancedClassInstanceContext;
|
||||
import com.a.eye.skywalking.api.plugin.interceptor.enhance.InstanceMethodInvokeContext;
|
||||
import com.a.eye.skywalking.api.plugin.interceptor.enhance.MethodInterceptResult;
|
||||
import com.a.eye.skywalking.sniffer.mock.context.MockTracerContextListener;
|
||||
import com.a.eye.skywalking.sniffer.mock.context.SegmentAssert;
|
||||
import com.a.eye.skywalking.trace.LogData;
|
||||
import com.a.eye.skywalking.trace.Span;
|
||||
import com.a.eye.skywalking.trace.TraceSegment;
|
||||
import com.a.eye.skywalking.trace.TraceSegmentRef;
|
||||
import com.a.eye.skywalking.trace.tag.Tags;
|
||||
|
||||
import org.hamcrest.CoreMatchers;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.runners.MockitoJUnitRunner;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import static org.hamcrest.CoreMatchers.is;
|
||||
import static org.hamcrest.MatcherAssert.assertThat;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.class)
|
||||
public class TomcatInterceptorTest {
|
||||
|
||||
private TomcatInterceptor tomcatInterceptor;
|
||||
private MockTracerContextListener contextListener;
|
||||
|
||||
@Mock
|
||||
private HttpServletRequest request;
|
||||
@Mock
|
||||
private HttpServletResponse response;
|
||||
@Mock
|
||||
private EnhancedClassInstanceContext classInstanceContext;
|
||||
@Mock
|
||||
private InstanceMethodInvokeContext methodInvokeContext;
|
||||
@Mock
|
||||
private MethodInterceptResult methodInterceptResult;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
tomcatInterceptor = new TomcatInterceptor();
|
||||
contextListener = new MockTracerContextListener();
|
||||
|
||||
TracerContext.ListenerManager.add(contextListener);
|
||||
|
||||
when(request.getRequestURI()).thenReturn("/test/testRequestURL");
|
||||
when(request.getRequestURL()).thenReturn(new StringBuffer("http://localhost:8080/test/testRequestURL"));
|
||||
when(response.getStatus()).thenReturn(200);
|
||||
when(methodInvokeContext.allArguments()).thenReturn(new Object[]{request, response});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithoutSerializedContextData() {
|
||||
tomcatInterceptor.beforeMethod(classInstanceContext, methodInvokeContext, methodInterceptResult);
|
||||
tomcatInterceptor.afterMethod(classInstanceContext, methodInvokeContext, null);
|
||||
|
||||
contextListener.assertSize(1);
|
||||
contextListener.assertTraceSegment(0, new SegmentAssert() {
|
||||
@Override
|
||||
public void call(TraceSegment traceSegment) {
|
||||
assertThat(traceSegment.getSpans().size(), is(1));
|
||||
Span span = traceSegment.getSpans().get(0);
|
||||
assertHttpSpan(span);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithSerializedContextData() {
|
||||
when(request.getHeader(TomcatInterceptor.HEADER_NAME_OF_CONTEXT_DATA)).thenReturn("302017.1487666919810.624424584.17332.1.1|1");
|
||||
|
||||
tomcatInterceptor.beforeMethod(classInstanceContext, methodInvokeContext, methodInterceptResult);
|
||||
tomcatInterceptor.afterMethod(classInstanceContext, methodInvokeContext, null);
|
||||
|
||||
contextListener.assertSize(1);
|
||||
contextListener.assertTraceSegment(0, new SegmentAssert() {
|
||||
@Override
|
||||
public void call(TraceSegment traceSegment) {
|
||||
assertThat(traceSegment.getSpans().size(), is(1));
|
||||
Span span = traceSegment.getSpans().get(0);
|
||||
assertHttpSpan(span);
|
||||
assertTraceSegmentRef(traceSegment.getPrimaryRef());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWithOccurException(){
|
||||
tomcatInterceptor.beforeMethod(classInstanceContext, methodInvokeContext, methodInterceptResult);
|
||||
tomcatInterceptor.handleMethodException(new RuntimeException(), classInstanceContext, methodInvokeContext);
|
||||
tomcatInterceptor.afterMethod(classInstanceContext, methodInvokeContext, null);
|
||||
|
||||
contextListener.assertSize(1);
|
||||
contextListener.assertTraceSegment(0, new SegmentAssert() {
|
||||
@Override
|
||||
public void call(TraceSegment traceSegment) {
|
||||
assertThat(traceSegment.getSpans().size(), is(1));
|
||||
Span span = traceSegment.getSpans().get(0);
|
||||
assertHttpSpan(span);
|
||||
assertThat(span.getLogs().size(), is(1));
|
||||
assertSpanLog(span.getLogs().get(0));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void assertSpanLog(LogData logData) {
|
||||
assertThat(logData.getFields().size(), is(3));
|
||||
assertThat(logData.getFields().get("error.kind"), CoreMatchers.<Object>is(RuntimeException.class.getName()));
|
||||
assertNull(logData.getFields().get("message"));
|
||||
}
|
||||
|
||||
private void assertTraceSegmentRef(TraceSegmentRef ref) {
|
||||
assertThat(ref.getSpanId(), is(1));
|
||||
assertThat(ref.getTraceSegmentId(), is("302017.1487666919810.624424584.17332.1.1"));
|
||||
}
|
||||
|
||||
private void assertHttpSpan(Span span) {
|
||||
assertThat(span.getOperationName(), is("/test/testRequestURL"));
|
||||
assertThat(Tags.COMPONENT.get(span), is("Tomcat"));
|
||||
assertThat(Tags.URL.get(span), is("http://localhost:8080/test/testRequestURL"));
|
||||
assertThat(Tags.STATUS_CODE.get(span), is(200));
|
||||
assertTrue(Tags.SPAN_LAYER.isHttp(span));
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() throws Exception {
|
||||
TracerContext.ListenerManager.remove(new MockTracerContextListener());
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Reference in New Issue