skywalking源码分析第二十篇一agent端实战之tomcat插件

定义插件

  • 拦截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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值