英文:
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
,以便在/jms
,onMessage()
, 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
<<<<<<<<< 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
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("/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); }
}
}
The Error Handler:
@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);
}
}
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<?> 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(() -> log.info("Im inside thread 2")); //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 = "test-queue", concurrency = "5")
void onMessage(TextMessage message) throws JMSException{
//..
throw new MyException("Some Error",tracer.currentSpan()); // <-- 传递当前的 span
}
}
这样你可以在JmsListenerErrorHandler
中获取它:
@Override
public void handleError(Throwable t) {
if(t.getCause() instanceof MyException){
MyException mEx = (MyException) t.getCause();
log.info("失败的 span: {}",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 = "test-queue", concurrency = "5")
void onMessage(TextMessage message) throws JMSException{
//..
throw new MyException("Some Error",tracer.currentSpan()); // <-- 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("Failing span: {}",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
}
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论