You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在HttpRequestExecutingMessageHandler中记录超时错误

问题描述

在使用HttpRequestExecutingMessageHandler时,希望在重试日志输出前记录具体的超时错误异常堆栈信息。当前已配置重试逻辑,但仅能看到Spring Retry框架的DEBUG级重试流程日志,无法获取详细的超时异常详情,期望输出包含完整堆栈的ERROR级异常日志。

当前配置代码

@Bean
public HttpRequestExecutingMessageHandler httpRequestExecutingMessageHandler(ApplicationProperties properties){
    HttpRequestExecutingMessageHandler handler = new HttpRequestExecutingMessageHandler(properties.getWebserviceAppendUrl());
    handler.setExpectedResponseType(String.class);
    handler.setHttpMethod(HttpMethod.POST);
    return handler;
}

@Bean
public IntegrationFlow sendRequestFlow(HttpRequestExecutingMessageHandler httpRequestExecutingMessageHandler,
                                       RequestHandlerRetryAdvice requestHandlerRetryAdvice){
    return IntegrationFlows.from(requestChannel())
            .enrichHeaders(h-> h.header(HttpHeaders.CONTENT_TYPE, "application/json"))
            .handle(httpRequestExecutingMessageHandler, e->e.advice(requestHandlerRetryAdvice))
            .log(LoggingHandler.Level.INFO, "x.xxx.client.config.DevConfig", m-> "Webservice response status code: " + m.getHeaders().get("http_statusCode"))
            .get();
}

@Bean
public RequestHandlerRetryAdvice requestHandlerRetryAdvice(RetryTemplate retryTemplate){
    RequestHandlerRetryAdvice advice = new RequestHandlerRetryAdvice();
    advice.setRetryTemplate(retryTemplate);
    ErrorMessageSendingRecoverer errorMessageSendingRecoverer = new ErrorMessageSendingRecoverer();
    errorMessageSendingRecoverer.setChannel(recoveryChannel());
    advice.setRecoveryCallback(errorMessageSendingRecoverer);
    return advice;
}

@Bean
public RetryTemplate retryTemplate(){
    RetryTemplate retryTemplate = new RetryTemplate();
    SimpleRetryPolicy retryPolicy = new SimpleRetryPolicy();
    retryPolicy.setMaxAttempts(3);
    retryTemplate.setRetryPolicy(retryPolicy);
    FixedBackOffPolicy fixedBackOffPolicy = new FixedBackOffPolicy();
    fixedBackOffPolicy.setBackOffPeriod(60_000L);
    retryTemplate.setBackOffPolicy(fixedBackOffPolicy);
    return retryTemplate;
}

当前超时异常日志

DEBUG 2024-01-05 16:19:17,191 [scheduling-1] o.s.r.s.RetryTemplate - Retry: count=0 
DEBUG 2024-01-05 16:20:19,323 [scheduling-1] o.s.r.s.RetryTemplate - Checking for rethrow: count=1 
DEBUG 2024-01-05 16:20:19,323 [scheduling-1] o.s.r.s.RetryTemplate - Retry: count=1 
DEBUG 2024-01-05 16:21:21,340 [scheduling-1] o.s.r.s.RetryTemplate - Checking for rethrow: count=2 
DEBUG 2024-01-05 16:21:21,340 [scheduling-1] o.s.r.s.RetryTemplate - Retry: count=2 
DEBUG 2024-01-05 16:21:23,357 [scheduling-1] o.s.r.s.RetryTemplate - Checking for rethrow: count=3 
DEBUG 2024-01-05 16:21:23,358 [scheduling-1] o.s.r.s.RetryTemplate - Retry failed last attempt: count=3 
WARN 2024-01-05 16:21:23,359 [scheduling-1] o.s.i.h.LoggingHandler - Retrying failed, fallback to recovery flow

期望日志格式

ERROR 2024-01-03 13:16:14,220 [scheduling-1] i.b.i.f.c.c.MainConfig - Exception while hitting webservice: I/O error on POST request for "http://somehost/api/v1": Connection timed out: connect; nested exception is java.net.ConnectException: Connection timed out: connect org.springframework.web.client.ResourceAccessException: I/O error on POST request for "http://somehost/api/v1": Connection timed out: connect; nested exception is java.net.ConnectException: Connection timed out: connect
    at org.springframework.web.client.RestTemplate.doExecute(RestTemplate.java:791)
    at org.springframework.web.client.RestTemplate.exchange(RestTemplate.java:676)
Caused by: java.net.ConnectException: Connection timed out: connect
    at java.net.DualStackPlainSocketImpl.connect0(Native Method)
    at java.net.DualStackPlainSocketImpl.socketConnect(Unknown Source)

需求:使用Spring Integration内置组件实现,无需自定义消息处理器。


解决方案

可以通过以下两种Spring Integration内置方式实现异常日志记录:

1. 给RequestHandlerRetryAdvice配置异常日志记录器

RequestHandlerRetryAdvice原生支持设置ExceptionLogger,可在每次重试失败时立即记录完整异常信息:

@Bean
public RequestHandlerRetryAdvice requestHandlerRetryAdvice(RetryTemplate retryTemplate){
    RequestHandlerRetryAdvice advice = new RequestHandlerRetryAdvice();
    advice.setRetryTemplate(retryTemplate);
    // 添加自定义异常日志记录逻辑
    advice.setExceptionLogger((message, throwable) -> {
        LogFactory.getLog("x.xxx.client.config.DevConfig")
                  .error("Exception while hitting webservice: " + throwable.getMessage(), throwable);
    });
    ErrorMessageSendingRecoverer errorMessageSendingRecoverer = new ErrorMessageSendingRecoverer();
    errorMessageSendingRecoverer.setChannel(recoveryChannel());
    advice.setRecoveryCallback(errorMessageSendingRecoverer);
    return advice;
}

配置后,每次触发重试前都会先输出ERROR级别的异常日志,包含完整堆栈信息,再执行重试流程。

2. 利用recoveryChannel实现异常日志记录

通过给recoveryChannel配置处理流,在所有重试失败后统一记录异常:

@Bean
public IntegrationFlow recoveryFlow() {
    return IntegrationFlows.from(recoveryChannel())
            .log(LoggingHandler.Level.ERROR, "x.xxx.client.config.DevConfig", 
                 message -> "Exception while hitting webservice: " + message.getPayload().getMessage(),
                 message -> message.getPayload()) // 携带异常堆栈信息
            .get();
}

@Bean
public MessageChannel recoveryChannel() {
    return new DirectChannel();
}

这种方式会在重试耗尽后,将错误消息发送到recoveryChannel,通过日志处理器输出完整异常详情。

3. 可选:调整日志级别

确保目标日志类x.xxx.client.config.DevConfig的日志级别设置为ERROR或更高,同时根据需求保留o.s.r.s.RetryTemplate的DEBUG级别,即可同时看到重试流程和异常详情日志。


内容的提问来源于stack exchange,提问作者of32 inc

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 16:14:55