skywalking 支持@JmsListener日志tid输出

项目中出现ActiveMQ consumer log中出现断续情况,没有traceId输出

这样根本连不成一个链路来追踪问题了

原因:链路从主线程到子线程没有传递

解决方案:

@TraceCrossThread

@TraceCrossThread为skywalking提供的工具包中的注解,你可以认为其有一定的侵入性。使用方式则是在那个线程类上加上该注解,在类加载时,其构造方法会被代理agent做一次增强

@TraceCrossThread
public class MyRunnable implements Runnable{
  @Override
  public void run() {
      System.out.println(TraceContext.traceId());
      doNothing();
  }
}

但是这种方式要去改ActiveMQ源码,不太合适

apm-jdk-threading-plugin

apm-jdk-threading-plugin这是官方提供的一个插件,是真正的无侵入了,使用方式也很简单,这个插件位于${skywalking_dir}/agent/bootstrap-plugins目录下,我们需要做的就是将其复制到${skywalking_dir}/agent/plugins目录下即可。

另外,还需要对代理的配置进行修改${skywalking_dir}/agent/config/agent.config,告诉代理对那些包下的线程池进行增强,配置如下:

plugin.jdkthreading.threading_class_prefixes=${SW_PLUGIN_JDKTHREADING_THREADING_CLASS_PREFIXES:}

可以采用配置覆盖的方式,系统环境变量配置:

SW_PLUGIN_JDKTHREADING_THREADING_CLASS_PREFIXES=org.springframework.jms.listener.DefaultMessageListenerContainer

参考:配置覆盖

ActiveMQ消费消息日志保持tid输出

还是为了借据ActiveMQ consumer 的log 输出中没有tid,在了解了skywalking plugin的源码后,对activemq-5.x-plugin官方插件进行改造

  1. 添加ActiveMessageListenerInstrumentation类,用于启用探针

#skywalking-plugin.def
activemq-5.x=org.apache.skywalking.apm.plugin.activemq.define.ActiveMessageListenerInstrumentation
public class ActiveMessageListenerInstrumentation extends ClassInstanceMethodsEnhancePluginDefine {
    public static final String INTERCEPTOR_CLASS = "org.apache.skywalking.apm.plugin.activemq.ActiveMessageListenerInterceptor";
    public static final String ENHANCE_CLASS_CONSUMER = "org.springframework.jms.listener.DefaultMessageListenerContainer";
    public static final String ENHANCE_METHOD_DISPATCH = "doExecuteListener";

    @Override
    public ConstructorInterceptPoint[] getConstructorsInterceptPoints() {
        return new ConstructorInterceptPoint[0];
    }

    @Override
    public InstanceMethodsInterceptPoint[] getInstanceMethodsInterceptPoints() {
        return new InstanceMethodsInterceptPoint[] {
            new InstanceMethodsInterceptPoint() {
                @Override
                public ElementMatcher<MethodDescription> getMethodsMatcher() {
                    return named(ENHANCE_METHOD_DISPATCH);
                }

                @Override
                public String getMethodsInterceptor() {
                    return INTERCEPTOR_CLASS;
                }

                @Override
                public boolean isOverrideArgs() {
                    return false;
                }
            }
        };
    }

    @Override
    protected ClassMatch enhanceClass() {
        return MultiClassNameMatch.byMultiClassMatch(ENHANCE_CLASS_CONSUMER);
    }
}
  1. 关键代码:

ENHANCE_CLASS_CONSUMER = "org.springframework.jms.listener.DefaultMessageListenerContainer";
ENHANCE_METHOD_DISPATCH = "doExecuteListener";

主要了解了Spring jms的源码后得知DefaultMessageListenerContainer是重用线程,用于异步消息监听,真正消费消息是最后调用了他的父类AbstractMessageListenerContainer#doExecuteListener方法,所以此做探针对该方法进行代理

  1. 具体方法拦截逻辑

public class ActiveMessageListenerInterceptor implements InstanceMethodsAroundInterceptor {
​
  public static final String OPERATE_NAME_PREFIX = "ActiveMQ/";
  public static final String CONSUMER_OPERATE_NAME_SUFFIX = "/OnMessage";
​
  @Override
  public void beforeMethod(EnhancedInstance objInst, Method method, Object[] allArguments, Class<?>[] argumentsTypes,
      MethodInterceptResult result) throws Throwable {
      ContextCarrier contextCarrier = new ContextCarrier();
      Message message = (Message) allArguments[1];
      CarrierItem next = contextCarrier.items();
      while (next.hasNext()) {
          next = next.next();
          Object propertyValue = message.getStringProperty(next.getHeadKey());
          if (propertyValue != null) {
              next.setHeadValue(propertyValue.toString());
          }
      }
      AbstractSpan localSpan = ContextManager.createEntrySpan(OPERATE_NAME_PREFIX + message.getJMSDestination() + CONSUMER_OPERATE_NAME_SUFFIX, contextCarrier);
      localSpan.setComponent(ComponentsDefine.ACTIVEMQ_CONSUMER);
      SpanLayer.asMQ(localSpan);
      ContextManager.extract(contextCarrier);
  }
 
···以下省略

拦截逻辑主要是对Message中的属性进行解析,因为skywalking会在mq头中加上相关属性。

通过ContestManager#createEntrySpan方法创建EntrySpan,最后使用ContextManager#extract,建立分布式调用关联;

  • 参考1:org.apache.skywalking.apm.plugin.activemq.ActiveMQConsumerInterceptor源码

  1. 最后package后替换插件到:/app/skywalking/agent/plugins/apm-activemq-5.x-plugin-xxx.jar

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值