如何在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
相关产品推荐
相关产品推荐

