Spring Sleuth – JMS ErrorHandler 上的追踪中断

huangapple go评论75阅读模式
英文:

Spring Sleuth - broken tracing on JMS ErrorHandler

问题

这是您提供的内容的翻译:

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

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

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

我运行了应用程序并访问了localhost:8080/jms端点。在下面的日志中,跟踪ID没有传播到JmsListenerErrorHandler类中,并且在对/test的GET调用中创建了一个新的跟踪ID

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具有必要的“instrument”逻辑,可以拦截并传播跟踪/跨度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

对于正常线程执行,它起作用:

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

有人已经找到了如何拦截ErrorHandler以传递TraceId吗?

英文:

I've a simple example https://github.com/gtiwari333/sleuth-jms-broken-tracing/tree/master that uses Spring Sleuth with JMS.

Here, the call to /jms endpoint queues a message and on receipt of the message at onMessage method, we are doing a GET call to /test and throwing MyException. We expect the trace id pass along to the ErrorHandler so that we see the same traceId in the log between the /jms, onMessage(), handleError(), and /test endpoints.

What I'm getting now/How to get the error:

I ran the app and hit the localhost:8080/jms endpoint. In the log below, the TraceId is not propagated in JmsListenerErrorHandler class and a new TraceId created for the GET call to /test

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  
&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt; FINE UPTO HERE
2020-08-04 17:55:24.332  INFO [,,,] 16956 --- [enerContainer-1] sleuth.SleuthApplication                 : handling error by calling another endpoint ..     
&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt; 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  
&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt; new trace id received

It looks the JMS handles the receive/processing of new messages in a new thread. Sleuth has the necessary ‘instrument’ logic to intercept and propagate the Trace/Span ids to @JmsListener code but it doesn’t propagate to the org.springframework.util.ErrorHandler.

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

The Code:

The @RestController and @JmsListener:

@RestController
static class Ctrl {

    @Autowired RestTemplate restTemplate;
    @Autowired JmsTemplate jmsTemplate;

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

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

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

The Error Handler:

@Component
static class JmsListenerErrorHandler implements ErrorHandler {

    @Autowired RestTemplate restTemplate;

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

The JMS Config:

@Configuration
@EnableJms
static class ActiveMqConfig implements JmsListenerConfigurer {

    @Autowired ErrorHandler jmsListenerErrorHandler;

    @Autowired ConnectionFactory connectionFactory;

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

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

What I tried:(to make it a complete SO question)

Its in the PR: https://github.com/gtiwari333/sleuth-jms-broken-tracing/pull/1/files
Here, I tried to use create a custom Executor bean wrapped by LazyTraceThreadPoolTaskExecutor and tried to pass it to JmsListenerContainerFactory

Its working for a normal thread execution but not for the JMS stuff.

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

Has someone already figured out how to intercept the ErrorHandler to pass the TraceId?

答案1

得分: 3

关于@JmsListener的仪器化存在一个已开放的问题,所以我认为目前不受支持。

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

@RestController
static class Ctrl {
    @Autowired
    private Tracer tracer;
    // ...
    @JmsListener(destination = &quot;test-queue&quot;, concurrency = &quot;5&quot;)
    void onMessage(TextMessage message) throws JMSException{
        //..
        throw new MyException(&quot;Some Error&quot;,tracer.currentSpan()); // &lt;-- 传递当前的 span
    }
}

这样你可以在JmsListenerErrorHandler中获取它:

@Override
public void handleError(Throwable t) {
    if(t.getCause() instanceof MyException){
        MyException mEx = (MyException) t.getCause();
        log.info(&quot;失败的 span: {}&quot;,mEx.getSpan());
    }
    //...
}

MyException 类:

class MyException extends RuntimeException {
    private final Span span;
    public MyException(String msg, Span span) {
        super(msg);
        this.span=span;
    }
    // 获取 Span 的 Getter
}
英文:

There is an open issue about the instrumentation of @JmsListener. So I guess at the moment is not supported.

A possible solution is to pass the Span in the exception:

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

So you can get it in JmsListenerErrorHandler:

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

MyException class:

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

huangapple
  • 本文由 发表于 2020年8月5日 11:17:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/63257909.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定