skywalking源码分析第二十篇一agent端实战之tomcat插件
定义插件拦截StandardHost的的请求处理valve: StandardHostValve,invoke方法此外还拦截异常处理方法[本文不做介绍]public class TomcatInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {private static final String ENHANCE_CLAS
·
定义插件
- 拦截StandardHost的的请求处理valve: StandardHostValve,invoke方法
- 此外还拦截异常处理方法[本文不做介绍]
public class TomcatInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
private static final String ENHANCE_CLASS = "org.apache.catalina.core.StandardHostValve";
private static final String INVOKE_INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.tomcat78x.TomcatInvokeInterceptor";
private static final String EXCEPTION_INTERCEPT_CLASS = "org.apache.skywalking.apm.plugin.tomcat78x.TomcatExceptionInterceptor";
@Override
protected ClassMatch enhanceClass() {
return byName(ENHANCE_CLASS);
}
@Override
public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
return null;
}
@Override
public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
return new InstanceMethodsInterceptPoint[] {
new InstanceMethodsInterceptPoint() {
切入位置
|
socket----------engine-> host->context->wrapper->mvc [大家自行了解,这里不再介绍tomcat相关原理]
@Override
public ElementMatcher<MethodDescription> getMethodsMatcher() {
return named("invoke");
}
@Override
public String getMethodsInterceptor() {
return INVOKE_INTERCEPT_CLASS;
}
@Override
public boolean isOverrideArgs() {
return false;
}
},
new InstanceMethodsInterceptPoint() {
@Override public ElementMatcher<MethodDescription> getMethodsMatcher() {
return named("throwable");
}
@Override public String getMethodsInterceptor() {
return EXCEPTION_INTERCEPT_CLASS;
}
@Override public boolean isOverrideArgs() {
return false;
}
}
};
}
}
定义拦截器
- beforeMethod创建Context和Segment
- beforeMethod创建EntrySpan
- 为span添加一些kv键值对[特定和普通]
- 特定的key比如 span.setComponent
- 普通的key通过TagValuePair添加[k和v都是自定义]
skywalking-ui 可以查询普通的特殊键值对
- afterMethod 通过ContextManager.stopSpan完成数据上报
- afterMethod 通过ContextManager清理上下文信息
public class TomcatInvokeInterceptor implements InstanceMethodsAroundInterceptor {
private static boolean IS_SERVLET_GET_STATUS_METHOD_EXIST;
private static final String SERVLET_RESPONSE_CLASS = "javax.servlet.http.HttpServletResponse";
private static final String GET_STATUS_METHOD = "getStatus";
static {
IS_SERVLET_GET_STATUS_METHOD_EXIST = MethodUtil.isMethodExist(TomcatInvokeInterceptor.class.getClassLoader(), SERVLET_RESPONSE_CLASS, GET_STATUS_METHOD);
}
@Override public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments,
Class<?>[] argumentsTypes, MethodInterceptResult result) throws Throwable {
HttpServletRequest request = (HttpServletRequest)allArguments[0];
ContextCarrier contextCarrier = new ContextCarrier();
// http请求头中如果设置sw8 sw8-correlation sw8-x 则加入到ContextCarrier
CarrierItem next = contextCarrier.items();
while (next.hasNext()) {
next = next.next();
next.setHeadValue(request.getHeader(next.getHeadKey()));
}
创建EntrySpan 内部创建TracerContext和Segment 并将span加入TracerContext.activeSpanStack
设置AbstractSpan的栈深度stackDepth和最大深度currentMaxDepth为1
AbstractSpan span = ContextManager.createEntrySpan(request.getRequestURI(), contextCarrier);
为span添加一个TagValuePair 【将来skywalking-ui展示: url】
Tags.URL.set(span, request.getRequestURL().toString());
为span添加一个TagValuePair 【将来skywalking-ui展示: http.method】
Tags.HTTP.METHOD.set(span, request.getMethod());
为span设置组件名 【将来skywalking-ui展示: 组件】
span.setComponent(ComponentsDefine.TOMCAT);
为span添加一个图层 【将来skywalking-ui展示: 组件】
SpanLayer.asHttp(span);
}
@Override public Object afterMethod(EnhancedInstance objInst, Method method, Object[] allArguments,
Class<?>[] argumentsTypes, Object ret) throws Throwable {
HttpServletResponse response = (HttpServletResponse)allArguments[1];
// 获取当前span
AbstractSpan span = ContextManager.activeSpan();
根据服务设置tag
if (IS_SERVLET_GET_STATUS_METHOD_EXIST && response.getStatus() >= 400) {
span.errorOccurred();
Tags.STATUS_CODE.set(span, Integer.toString(response.getStatus()));
}
span从栈中转入Segment 进行trace上报
ContextManager.stopSpan();
ContextManager.getRuntimeContext().remove(Constants.FORWARD_REQUEST_FLAG);
return ret;
}
@Override public void handleMethodException(EnhancedInstance objInst, Method method, Object[] allArguments,
Class<?>[] argumentsTypes, Throwable t) {
发生异常记录错误[skywalking-ui可查阅]
AbstractSpan span = ContextManager.activeSpan();
span.log(t);
span.errorOccurred();
}
}
插件生效
- 在skywalking-plugin.def中定义插件,启动时扫描插件并加载
扩展点:UI一Segment
- 每一行代表一个span,一起形成Segment
扩展点:UI一Span
总结
- EntrySpan可能会被多个插件共用
- 则部分属性以第一个插件为准(比如tomcat),部分属性以最后一个为准(比如mvc)
- span的componentId 、componentName 、layer 、operationName 、operationId 等属性往往以最后一个插件为准,因为最后一个插件往往对场景更加细分,能更好的表达span自身所代表的含义
更多推荐
所有评论(0)