【问题标题】:Spring Sleuth - broken tracing on JMS ErrorHandlerSpring Sleuth - JMS ErrorHandler 上的跟踪中断
【发布时间】:2020-11-25 04:49:04
【问题描述】:

我有一个简单的示例 https://github.com/gtiwari333/sleuth-jms-broken-tracing/tree/master,它使用带有 JMS 的 Spring Sleuth。

在这里,对/jms 端点的调用将消息排入队列,并且在onMessage 方法收到消息后,我们正在对/test 进行GET 调用并抛出MyException。我们希望跟踪 ID 传递到 ErrorHandler,以便我们在日志中看到 /jmsonMessage()handleError()/test 端点之间的相同 traceId。

我现在得到什么/如何得到错误:

我运行了应用程序并点击了localhost:8080/jms 端点。在下面的日志中,TraceId 未在 JmsListenerErrorHandler 类中传播,并且为对 /test 的 GET 调用创建了一个新的 TraceId

2020-08-04 17:55:24.212  INFO [,225c47fb814f6584,225c47fb814f6584,true] 16956 --- [nio-8080-exec-1] sleuth.SleuthApplication                 : Queuing message ...
2020-08-04 17:55:24.282  INFO [,225c47fb814f6584,eac851f1650ae8a6,true] 16956 --- [enerContainer-1] sleuth.SleuthApplication                 : JMS message received SOME MESSAGE !!!
2020-08-04 17:55:24.321  INFO [,225c47fb814f6584,612a7956f6b29a01,true] 16956 --- [nio-8080-exec-3] sleuth.SleuthApplication                 : test1 called  
<<<<<<<<< FINE UPTO HERE
2020-08-04 17:55:24.332  INFO [,,,] 16956 --- [enerContainer-1] sleuth.SleuthApplication                 : handling error by calling another endpoint ..     
<<<<<<<<< new thread started and lost tracing
2020-08-04 17:55:24.336  INFO [,4c163d0997076729,4c163d0997076729,true] 16956 --- [nio-8080-exec-2] sleuth.SleuthApplication                 : test1 called  
<<<<<<<<< new trace id received

看起来 JMS 在新线程中处理新消息的接收/处理。 Sleuth 具有必要的“仪器”逻辑来拦截 Trace/Span id 并将其传播到 @JmsListener 代码,但它不会传播到 org.springframework.util.ErrorHandler

  • org.springframework.jms.listener.DefaultMessageListenerContainer.AsyncMessageListenerInvoker
  • org.springframework.jms.listener.AbstractPollingMessageListenerContainer#doReceiveAndExecute

守则:

@RestController 和@JmsListener:

@RestController
static class Ctrl {

    @Autowired RestTemplate restTemplate;
    @Autowired JmsTemplate jmsTemplate;

    @GetMapping("/test")
    void test() {
        log.info("test1 called");
    }

    @GetMapping("/jms")
    void jms() {
        log.info("Queuing message ...");
        jmsTemplate.convertAndSend("test-queue", "SOME MESSAGE !!!");
    }

    @JmsListener(destination = "test-queue", concurrency = "5")
    void onMessage(TextMessage message) throws JMSException {
        log.info("JMS message received {}", message.getText());
        restTemplate.getForEntity("http://localhost:8080/test", Void.class); //-->it works
        throw new MyException("Some Error");  //-->it doesn't
    }
    static class MyException extends RuntimeException {
        public MyException(String msg) { super(msg); }
    }
}

错误处理程序:

@Component
static class JmsListenerErrorHandler implements ErrorHandler {

    @Autowired RestTemplate restTemplate;

    @Override
    public void handleError(Throwable t) {
        log.info("handling error by calling another endpoint .."); //1....tracing is lost here
        restTemplate.getForEntity("http://localhost:8080/test", Void.class);
    }
}

JMS 配置:

@Configuration
@EnableJms
static class ActiveMqConfig implements JmsListenerConfigurer {

    @Autowired ErrorHandler jmsListenerErrorHandler;

    @Autowired ConnectionFactory connectionFactory;

    @Override
    public void configureJmsListeners(JmsListenerEndpointRegistrar registrar) {
        registrar.setContainerFactory(containerFactory());
    }

    @Bean
    JmsListenerContainerFactory<?> containerFactory() {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setErrorHandler(jmsListenerErrorHandler);
        return factory;
    }
}

我尝试了什么:(使它成为一个完整的 SO 问题)

在 PR 中:https://github.com/gtiwari333/sleuth-jms-broken-tracing/pull/1/files 在这里,我尝试使用创建一个由LazyTraceThreadPoolTaskExecutor 包裹的自定义Executor bean,并尝试将其传递给JmsListenerContainerFactory

它适用于正常的线程执行,但不适用于 JMS 的东西。

executor.execute(() -&gt; log.info("Im inside thread 2")); //it works

有人已经想好如何拦截ErrorHandler来传递TraceId了吗?

【问题讨论】:

  • 所有类都是static的任何原因?
  • 哦..它们是内部类。

标签: java spring spring-boot spring-cloud-sleuth


【解决方案1】:

关于@JmsListener 的检测有一个open issue。所以我猜目前不支持。

一种可能的解决方案是在异常中传递Span

@RestController
static class Ctrl {
    @Autowired
    private Tracer tracer;
    // ...
    @JmsListener(destination = "test-queue", concurrency = "5")
    void onMessage(TextMessage message) throws JMSException{
        //..
        throw new MyException("Some Error",tracer.currentSpan()); // <-- pass current span
    }
}

所以你可以在JmsListenerErrorHandler

@Override
public void handleError(Throwable t) {
    if(t.getCause() instanceof MyException){
        MyException mEx = (MyException) t.getCause();
        log.info("Failing span: {}",mEx.getSpan());
    }
    //...
}

MyException类:

class MyException extends RuntimeException {
    private final Span span;
    public MyException(String msg, Span span) {
        super(msg);
        this.span=span;
    }
    // Getter for the Span
}

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-08-24
  • 2021-12-30
  • 2021-09-03
  • 1970-01-01
  • 2014-04-28
  • 2017-06-08
  • 1970-01-01
相关资源
最近更新 更多