Spring @Retryable - 如何在调用时记录日志?

27

我使用 compile 'org.springframework.retry:spring-retry:1.2.2.RELEASE'Spring Boot 1.5.9.RELEASE一起。

已配置重试我的方法,它运行良好:

@Retryable(value = { IOException.class }, maxAttempts = 5, backoff = @Backoff(delay = 500))
public void someMethod(){...}

当重试发生时,如何输出一些特定的消息?

3个回答

45
您可以注册一个 RetryListener:
@Bean
public List<RetryListener> retryListeners() {
    Logger log = LoggerFactory.getLogger(getClass());

    return Collections.singletonList(new RetryListener() {

        @Override
        public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
            // The 'context.name' attribute has not been set on the context yet. So we have to use reflection.
            Field labelField = ReflectionUtils.findField(callback.getClass(), "val$label");
            ReflectionUtils.makeAccessible(labelField);
            String label = (String) ReflectionUtils.getField(labelField, callback);
            log.trace("Starting retryable method {}", label);
            return true;
        }

        @Override
        public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
            log.warn("Retryable method {} threw {}th exception {}",
                    context.getAttribute("context.name"), context.getRetryCount(), throwable.toString());
        }

        @Override
        public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
            log.trace("Finished retryable method {}", context.getAttribute("context.name"));
        }
    });

如果您不需要从所有三个拦截点记录日志,可以覆盖 RetryListenerSupport。例如:
@Bean
public List<RetryListener> retryListeners() {
    Logger log = LoggerFactory.getLogger(getClass());

    return Collections.singletonList(new RetryListenerSupport() {

        @Override
        public <T, E extends Throwable> void onError(
                RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
            log.warn("Retryable method {} threw {}th exception {}",
                    context.getAttribute("context.name"), 
                    context.getRetryCount(), throwable.toString());
        }
    });
}

3
当我启用实现RetryListenerSupport的Bean时,标记有@Retryable的方法停止被执行。有什么想法吗? - bforcer
@bforcer 你的项目中是否还有其他的RetryListener bean?也许它们会互相干扰。 - whistling_marmot
不,这是唯一实现RetryListener的bean。 - bforcer
对我来说,解决方案是在SpringBoot配置(主类)中添加RetryListener。 - Julian Stier
2
这个特殊字符 "val$label" 是什么?它是 Spring SpEL 吗?(ReflectionUtils.findField(callback.getClass(), "val$label");) - zy_sun

11

查看代码后发现org.springframework.retry.support.RetryTemplate执行重试操作的重试逻辑。此模板仅记录简单的内容,例如:

o.s.retry.support.RetryTemplate          : Retry: count=0
o.s.retry.support.RetryTemplate          : Checking for rethrow: count=1
o.s.retry.support.RetryTemplate          : Retry: count=1
o.s.retry.support.RetryTemplate          : Checking for rethrow: count=2
o.s.retry.support.RetryTemplate          : Retry: count=2
o.s.retry.support.RetryTemplate          : Checking for rethrow: count=3
o.s.retry.support.RetryTemplate          : Retry failed last attempt: count=3

如果您想记录特定异常,可以捕获异常并记录它,然后重新抛出。不幸的是,据我所知,在框架中没有办法记录自定义消息。

另一种方法是阴影实际拦截器负责调用您的方法,比如RetryOperationsInterceptor,但这不被建议。


2
另一种方法是检查RetryContext: (链接)
@Retryable(maxAttempts = 2, include = RuntimeException.class)
public void test() {
    log.info("test");
    if (RetrySynchronizationManager.getContext().getRetryCount() > 0) {
        log.warn(RetrySynchronizationManager.getContext());
    }
    throw new RuntimeException();
}

[重试上下文: 次数=1,最后异常=java.lang.RuntimeException,已用尽=false]


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接