Skip to content

Commit

Permalink
비동기 예외 처리
Browse files Browse the repository at this point in the history
  • Loading branch information
greeng00se committed Sep 19, 2023
1 parent 2491e31 commit 576e3f8
Showing 1 changed file with 15 additions and 14 deletions.
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
---
title: 비동기 예외 처리
slug: async-exception
title: 비동기 예외 로깅
slug: log-async-exception
tags: [async, exception]
---

### 개요
### 문제 상황

현재 트립드로우의 경로 이미지 생성 기능은 비동기로 처리되고 있다. 로그를 확인하는 도중 `@Async`가 적용된 메서드에서 예외가 발생하는 경우 로그가 정상적으로 출력되지 않는 문제가 발생했다.
확인해 보니 Spring의 `@ControllerAdvice` + `@ExceptionHandler`의 경우 동기 예외만 처리하고, 비동기 예외를 처리하지 않았기 때문에 발생한 문제였다.

확인해 보니 Spring의 `@ControllerAdvice` + `@ExceptionHandler`의 경우 동기 예외만 처리하고, 비동기 예외를 처리하지 않았다. 따라서 Spring에서 지원해 주는 `AsyncUncaughtExceptionHandler` 인터페이스를 구현해서 예외를 처리하는 클래스를 생성했다.
### 비동기 예외 발생시 로깅 설정

### 비동기 예외처리
Spring에서 지원해 주는 AsyncUncaughtExceptionHandler 인터페이스를 구현해서 예외를 처리하는 클래스를 생성했다.

```java title=AsyncExceptionHandler
@Slf4j
Expand All @@ -25,8 +26,8 @@ public class AsyncExceptionHandler implements AsyncUncaughtExceptionHandler {
}
```

해당 `AsyncExceptionHandler`경우 `AsyncConfigurer` 구현한 Configuration 클래스를 사용하여 등록할 수 있다. `getAsyncUncaughtExceptionHandler` 메서드를 오버라이딩하여 이전에 생성해 준 `AsyncExceptionHandler`를 반환하도록 설정했다.
이렇게 설정한다면 예외가 발생하는 경우 `AsyncUncaughtExceptionHandler`의 구현체인 `AsyncExceptionHandler`가 예외를 잡아서 처리를 해준다.
AsyncExceptionHandler의 경우 AsyncConfigurer를 구현한 Configuration 클래스를 사용하여 등록할 수 있다.
getAsyncUncaughtExceptionHandler() 메서드를 오버라이딩하여 AsyncExceptionHandler를 반환하도록 설정하면 된다.

```java title=AsyncConfig
@EnableAsync
Expand All @@ -40,16 +41,15 @@ public class AsyncConfig implements AsyncConfigurer {
}
```

### MDC 정보 연동 문제
이제 예외가 발생하는 경우 AsyncUncaughtExceptionHandler의 구현체인 AsyncExceptionHandler가 예외를 잡아서 처리를 해준다.

![./mdc-null.png](./mdc-null.png)
### MDC 정보 연동 문제

기존 예외가 발생할 때 실행 흐름을 추적하기 위해 MDC(Mapped Diagnostic Context)를 사용한다.
비동기 처리의 경우 별도의 스레드에서 동작하기 때문에 ThreadLocal 기반으로 동작하는 MDC의 정보를 얻어올 수 없었다.
트립드로우의 애플리케이션은 예외가 발생할 때 실행 흐름을 추적하기 위해 MDC(Mapped Diagnostic Context)를 사용하고 있다. 비동기 처리의 경우 별도의 스레드에서 동작하기 때문에 ThreadLocal 기반으로 동작하는 MDC의 정보를 얻어올 수 없었다.

이를 적절하게 Decorator 클래스를 설정하여 MDC의 정보를 복사해서 넘겨줄 수 있다.
![./mdc-null.png](./mdc-null.png)

다음과 같이 TaskDecorator를 구현한 클래스를 하나 생성하고, Task가 실행되기 전 MDC의 정보를 복사하도록 설정했다.
Spring 4.3 이상부터 제공되는 [TaskDecorator](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/task/TaskDecorator.html)를 이용하면 TaskExecutor를 커스터마이징 할 수 있다. TaskDecorator를 구현한 클래스를 하나 생성하고, Task가 실행되기 전 MDC의 정보를 복사하도록 설정한다.

```java title=MdcTaskDecorator
public class MdcTaskDecorator implements TaskDecorator {
Expand Down Expand Up @@ -105,4 +105,5 @@ public class AsyncConfig implements AsyncConfigurer {

[spring async, baeldung](https://www.baeldung.com/spring-async)
[@Async will not call by @ControllerAdvice for global exception](https://stackoverflow.com/questions/61885358/async-will-not-call-by-controlleradvice-for-global-exception)
[Spring 의 동기, 비동기, 배치 처리시 항상 context 를 유지하고 로깅하기, 강남언니](https://blog.gangnamunni.com/post/mdc-context-task-decorator/)
[Spring 의 동기, 비동기, 배치 처리시 항상 context 를 유지하고 로깅하기, 강남언니](https://blog.gangnamunni.com/post/mdc-context-task-decorator/)
[TaskDecorator, Spring docs](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/task/TaskDecorator.html)

0 comments on commit 576e3f8

Please sign in to comment.