Spring Async Uncaught Exception handler

后端 未结 4 1412
不思量自难忘°
不思量自难忘° 2020-12-09 09:08
@Override
@Async
public void asyncExceptionTest() {
    int i=1/0;
}

How can I log this using Spring Async framework without having to put try catc

相关标签:
4条回答
  • 2020-12-09 09:34

    Update: Since Spring 4.1

    Since Spring 4.1 It is possible to have an AsyncUncaughtExceptionHandler for @Async void methods.

    Spring Reference Doc, Chapter 34.4.5 Exception management with @Async

    ... With a void return type however, the exception is uncaught and cannot be transmitted. For those cases, an AsyncUncaughtExceptionHandler can be provided to handle such exceptions.

    By default, the exception is simply logged. A custom AsyncUncaughtExceptionHandler can be defined via AsyncConfigurer or the task:annotation-driven XML element.

    (This feature was introduced after DD raised an impovement request: https://jira.spring.io/browse/SPR-8995 , see comments of this answer)


    Before Spring 4.1

    Looks like an missing feature how to handle exceptions of an void returning @Async Method. (I can not find any hint in the reference or java doc)

    What I can imagine of an solution: Try to use AspectJ to write some kind of wrapper arround all @Async methods that log the exceptions.

    For the log term, I would recommend to create an freature request in the spring bug tracker.

    0 讨论(0)
  • 2020-12-09 09:36

    You can use standard Spring AOP approach

    @Aspect
    @Component
    @Slf4j
    public class AsyncHandler {
    
       @Around("@annotation(org.springframework.scheduling.annotation.Async)")
       private Object handle(ProceedingJoinPoint pjp) throws Throwable {
           try {
               Object retVal = pjp.proceed();
               return retVal;
           } catch (Throwable e) {
               log.error("in ASYNC, method: " + pjp.getSignature().toLongString() + ", args: " + AppStringUtils.transformToWellFormattedJsonString(pjp.getArgs()) + ", exception: "+ e, e);
               throw e;
           }
       }
    
    }
    
    0 讨论(0)
  • 2020-12-09 09:44

    @Async methods can be configured with a custom Executor to log any thrown exceptions.

    The following code implements this pattern. Any method tagged with @Async will use the Executor returned by the method public Executor getAsyncExecutor(). This returns the HandlingExecutor which takes care of all logging (in this case it just prints the word "CAUGHT!" but you can replace with logging.

    @Configuration
    @EnableAsync
    public class ExampleConfig implements AsyncConfigurer {
        @Bean
        public Runnable testExec() {
            return new TestExec();
        }
    
        @Override
        public Executor getAsyncExecutor() {
            final ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
            executor.setCorePoolSize(7);
            executor.setMaxPoolSize(42);
            executor.setQueueCapacity(11);
            executor.setThreadNamePrefix("MyExecutor-");
            executor.initialize();
            return new HandlingExecutor(executor);
        }
    }
    
    public class HandlingExecutor implements AsyncTaskExecutor {
        private AsyncTaskExecutor executor;
    
        public HandlingExecutor(AsyncTaskExecutor executor) {
            this.executor = executor;
        }
    
        @Override
        public void execute(Runnable task) {
            executor.execute(task);
        }
    
        @Override
        public void execute(Runnable task, long startTimeout) {
            executor.execute(createWrappedRunnable(task), startTimeout);
        }
    
        @Override
        public Future<?> submit(Runnable task) {
            return executor.submit(createWrappedRunnable(task));
        }
    
        @Override
        public <T> Future<T> submit(final Callable<T> task) {
            return executor.submit(createCallable(task));
        }
    
        private <T> Callable<T> createCallable(final Callable<T> task) {
            return new Callable<T>() {
                @Override
                public T call() throws Exception {
                    try {
                        return task.call();
                    } catch (Exception e) {
                        handle(e);
                        throw e;
                    }
                }
            };
        }
    
        private Runnable createWrappedRunnable(final Runnable task) {
            return new Runnable() {
                @Override
                public void run() {
                    try {
                        task.run();
                    } catch (Exception e) {
                        handle(e);
                    }
                }
            };
        }
    
        private void handle(Exception e) {
            System.out.println("CAUGHT!");
        }
    }
    
    0 讨论(0)
  • 2020-12-09 09:50

    First off all, you should create a custom exception handler class like following;

    @Component
    public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
    
            private final Logger logger = LoggerFactory.getLogger(AsyncExceptionHandler.class);
    
            @Override
            public void handleUncaughtException(Throwable ex, Method method, Object... params) {
                logger.error("Unexpected asynchronous exception at : "
                        + method.getDeclaringClass().getName() + "." + method.getName(), ex);
            }
    
        }
    

    After that, you should set your customized exception handler class in your configuration like following;

    @Configuration
    @EnableAsync
    public class AsyncConfig extends AsyncConfigurerSupport {
    
        @Autowired
        private AsyncExceptionHandler asyncExceptionHandler;
    
        @Override
        public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
            return asyncExceptionHandler;
        }
    
    }
    

    Note : Injectable exception handler is an option. You can create a new instance for every exception. My advice is using Injection for exception handler class, because spring's default scope is singleton so there is no need to create new instance for every exception.

    0 讨论(0)
提交回复
热议问题