提交 82ce2ad2 编写于 作者: Z zhangxin

Modify the Tomcat plugin using the new API

上级 a86266b8
......@@ -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>
......
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);
}
}
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;
}
}
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);
}
}
package com.a.eye.skywalking.api.plugin.tomcat78x.define;
package com.a.eye.skywalking.plugin.tomcat78x.define;
import com.a.eye.skywalking.plugin.interceptor.ConstructorInterceptPoint;
import com.a.eye.skywalking.api.plugin.interceptor.ConstructorInterceptPoint;
import com.a.eye.skywalking.api.plugin.interceptor.InstanceMethodsInterceptPoint;
import com.a.eye.skywalking.plugin.interceptor.enhance.ClassInstanceMethodsEnhancePluginDefine;
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;
public class TomcatPluginDefine extends ClassInstanceMethodsEnhancePluginDefine {
/**
* {@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 "org.apache.catalina.core.StandardEngineValve";
return ENHANCE_CLASS;
}
@Override
......@@ -29,7 +45,7 @@ public class TomcatPluginDefine extends ClassInstanceMethodsEnhancePluginDefine
@Override
public String getMethodsInterceptor() {
return "com.a.eye.skywalking.plugin.tomcat78x.TomcatPluginInterceptor";
return INTERCEPT_CLASS;
}
}};
}
......
com.a.eye.skywalking.plugin.tomcat78x.define.TomcatPluginDefine
com.a.eye.skywalking.plugin.tomcat78x.define.TomcatInstrumentation
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());
}
}
\ No newline at end of file
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册