-
Notifications
You must be signed in to change notification settings - Fork 5
โ๏ธ BE LOGGING
ERROR โ RuntimeException ์์์น ๋ชปํ ์๋ฒ ์๋ฌ๋ฅผ handler ํจ์ ์คํ ์ ๊ธฐ๋กํ๋ค
WARN โ BanggoodException, MethodArgumenthotValidException, OauthException ๋ฑ ์ฌ์ฉ์ ์ ๋ ฅ ์๋ฌ๋ฅผ ํฌํจํ ์๋ฒ์์ ์ฝ๋์์ผ๋ก ์์ธ ์ฒ๋ฆฌ๋ฅผ ํ ์๋ฌ๋ฅผ handler ํจ์ ์คํ ์ ๊ธฐ๋กํ๋ค
INFO โ Service ํด๋์ค ์ Public ๋ฉ์๋ ์์ ์๋ฃ ์์ , ์ฆ ํจ์๊ฐ ๋๋๋ ์์ ์ ๊ธฐ๋กํ๋ค
โ ๊ฐ์ฅ ๋ณดํธ์ ์ธ ๋ก๊ทธ ์ ๋ต์ ์๋ฆฝํ์๋ค ์ถํ ์ถ๊ฐํ๊ฑฐ๋ ํ์ ์๋ ๋ก๊ทธ๊ฐ ์์ ๊ฒฝ์ฐ ๋ณ๊ฒฝํ๋ค
- ์์ฒญ ์๊ฐ(timestamp)
- ๋ก๊ทธ ์๋ณ์ (UUID)
- ์์ฒญ URL (type ํฌํจ)
public abstract class BaseLog {
private final LocalDateTime requestTime;
private final String requestUrl;
private final String uuid;
- ์๋ฌ ๋ฉ์์ง
- stackTrace
public class ErrorLog extends BaseLog {
private final String errorMessage;
private final String stackTrace;
- ๊ฒฝ๊ณ ๋ฉ์์ง
public class WarnLog extends BaseLog {
private final String warnMessage;
- ํธ์ถ ๋ฉ์๋ ์ด๋ฆ
public class InfoLog extends BaseLog {
private final String infoMethodName;
๋ก๊ทธ์ ๊ธฐ๋ฅ๊ณผ ์๋น์ค์ ๊ธฐ๋ฅ์ ๊ตฌ๋ถํ๊ธฐ ์ํด ์คํ๋ง AOP๋ฅผ ๋์ ํ์๋ค.
AOP์ ๊ฒฝ์ฐ, ๊ด์ฌ์ฌ ์ค์ฌ์ ํ๋ก๊ทธ๋๋ฐ์ ๋ปํ๋ค. ์ฆ, ๋น์ฆ๋์ค ๋ก์ง๊ณผ ์ธ์ ๊ด์ฌ์ฌ๋ฅผ ๊ตฌ๋ถํ ์ ์๋ค.
-
Logback.xml
<?xml version="1.0" encoding="UTF-8"?> <configuration> // ์ค์ ํ์ผ๊ณผ ๊ด๋ จ๋ ๊ตฌ๋ฌธ์ด๋ค <include resource="org/springframework/boot/logging/logback/defaults.xml"/> // defaults.xml ํ์ผ์ ์ผ๋ฐ์ ์ผ๋ก ๊ธฐ๋ณธ ๋ ๋ฒจ์ ๋ก๊ฑฐ ์ค์ , ํจํด ๋ ์ด์์, ๋ก๊ฑฐ์ ์ ์ญ ์์ค ๋ฑ์ ํฌํจํ๋ค <include resource="org/springframework/boot/logging/logback/console-appender.xml"/> // ์ฝ์์ ๋ก๊ทธ๋ฅผ ์ถ๋ ฅํ๊ธฐ ์ํ Appender ๊ตฌ์ฑ์ ํฌํจํ๋ค <appender name="INFO" class="ch.qos.logback.core.ConsoleAppender"> // appender๋ ๋ก๊ทธ ๋ฉ์์ง๋ฅผ ์ค์ ๋ก ์ถ๋ ฅํ ๋์(์: ์ฝ์, ํ์ผ, ์๊ฒฉ ์๋ฒ ๋ฑ)์ ์ง์ ํ๋ ๊ฐ์ฒด์ด๋ค // ์ฝ์๋ก ๋ก๊ทธ ๋ฉ์์ง๋ฅผ ์ถ๋ ฅํ๋ ์ญํ ์ ํ๋ ํด๋์ค <filter class="ch.qos.logback.classic.filter.LevelFilter"> // ํน์ ๋ก๊ทธ ๋ ๋ฒจ์ ๋ฐ๋ผ ๋ก๊ทธ ๋ฉ์์ง๋ฅผ ํํฐ๋งํ๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ ํด๋์ค <level>INFO</level> // ํํฐ๋งํ ๋์ ๋ก๊ทธ ๋ ๋ฒจ์ ์ง์ <onMatch>ACCEPT</onMatch> // ์ผ์นํ ๊ฒฝ์ฐ ํด์ผ ๋๋ ๊ฑธ ์ง์ <onMismatch>DENY</onMismatch> // ์ผ์นํ์ง ์์ ๊ฒฝ์ฐ ํด์ผ ๋๋ ๊ฑธ ์ง์ </filter> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss} %highlight(%-5level){BLUE} [%thread] %logger{36} - %msg%n</pattern> // %d{yyyy-MM-dd HH:mm:ss}: ๋ก๊ทธ๊ฐ ๊ธฐ๋ก๋ ๋ ์ง์ ์๊ฐ // %highlight(%-5level){BLUE}: ๋ก๊ทธ์ ๋ ๋ฒจ(INFO, WARN, ERROR ๋ฑ) ๋ก๊ทธ ๋ฉ์์ง์ ํน์ ๋ถ๋ถ์ ์์์ผ๋ก ๊ฐ์กฐ // [%thread]: ๋ก๊ทธ๋ฅผ ์ถ๋ ฅํ ์ค๋ ๋ ์ด๋ฆ. // %logger{36}: ๋ก๊ฑฐ ์ด๋ฆ์ ์ต๋ 36๊ธ์๊น์ง ํ์. // %msg%n: ๋ก๊ทธ ๋ฉ์์ง์ ๊ฐํ ๋ฌธ์. </encoder> </appender> <appender name="WARN" class="ch.qos.logback.core.ConsoleAppender"> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>WARN</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss} %highlight(%-5level){YELLOW} [%thread] %logger{36} - %msg%n</pattern> </encoder> </appender> <appender name="ERROR" class="ch.qos.logback.core.ConsoleAppender"> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>ERROR</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss} %highlight(%-5level){RED} [%thread] %logger{36} - %msg%n</pattern> </encoder> </appender> <root level="INFO"> // ์ต์์ ๋ก๊ฑฐ๋ ๋ชจ๋ ๋ก๊ฑฐ์ ๊ธฐ๋ณธ ์ค์ ์ ์ ์ ์ต์์ ๋ก๊ฑฐ๊ฐ ์๋ ๊ฒ ๊ถ์ฅ๋๋ ๋ฐฉ์ <appender-ref ref="INFO"/> // ํน์ Appender๋ฅผ ์ฐธ์กฐํ๋ ๋ถ๋ถ <appender-ref ref="WARN"/> <appender-ref ref="ERROR"/> </root> </configuration>
-
AOP๋ก ๊ธฐ๋ก
Spring์์ AOP๋ ํ๋ก์ ๋ฐฉ์์ผ๋ก ๋์ํ๋ค. (ํ๋ก์๋ ํ๊ฒ์ ๊ฐ์ธ์ ํ๊ฒ์ ์์ฒญ์ ๋์ ๋ฐ์์ฃผ๋ Wrapping ์ค๋ธ์ ํธ์ด๋ค)
@Aspect @Component public class LoggingAspect { private static final Logger log = LoggerFactory.getLogger(LoggingAspect.class); @AfterReturning("execution(public * com.bang_ggood..*Service.*(..)))") // Service ์ด๋ฆ์ด ๋ถ์ ๋ชจ๋ ํด๋์ค์ public ๋ฉ์๋๊ฐ ์คํ๋ ํ public void loggingInfo(JoinPoint joinPoint) { // joinPoint๋ ํด๋น ํจ์๋ฅผ ์ผ์ปซ๋ ๋ง if (RequestContextHolder.getRequestAttributes() instanceof ServletRequestAttributes) { // ๋ฆฌํ์คํธ ์๋ ๋งฅ๋ฝ์ ํํด์๋ง ๋ก๊น (Service ๋ ์ด์ด ํ ์คํธ์์๋ ์๋ X) HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); // ํด๋น request ์ ๋ณด๋ฅผ ๊ฐ๊ณ ์ด String methodName = joinPoint.getSignature().getName(); // ๋ฉ์๋ ์ด๋ฆ์ ๊ฐ๊ณ ์ด log.info(InfoLog.of(request, methodName).toString()); //info ๋ ๋ฒจ ๋ก๊น } } @Before("execution(* com.bang_ggood.handler.GlobalExceptionHandler.*(..)) &&" + "!execution(* com.bang_ggood.handler.GlobalExceptionHandler.handleRuntimeException(..)))") // handleRuntimeException ํจ์๋ฅผ ์ ์ธํ๊ณ // GlobalExceptionHandler์ ๋ชจ๋ ํจ์๊ฐ ์๋ํ๊ธฐ ์ ์คํํ๋ค public void loggingWarn(JoinPoint joinPoint) { Optional<Exception> exceptionOptional = Arrays.stream(joinPoint.getArgs()) .filter(arg -> arg instanceof Exception) .map(arg -> (Exception) arg) .findFirst(); // GlobalExceptionHandler ํจ์์ ํ๋ผ๋ฏธํฐ๋ก exception์ ๋ฐ๊ธฐ ๋๋ฌธ์ ๊ทธ๊ฑธ ๊ฐ๊ณ ์ฌ ์ ์๋ค if (exceptionOptional.isPresent() && RequestContextHolder.getRequestAttributes() != null) { // exception์ด ์๊ณ request๊ฐ ์๋ ๊ฒฝ์ฐ์๋ง Exception exception = exceptionOptional.get(); HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); log.warn(WarnLog.of(exception, request).toString()); } } @Before("execution(* com.bang_ggood.handler.GlobalExceptionHandler.handleRuntimeException(..)))") // handleRuntimeException ํจ์๊ฐ ์๋ํ๊ธฐ ์ ์คํํ๋ค public void loggingError(JoinPoint joinPoint) { Optional<Exception> exceptionOptional = Arrays.stream(joinPoint.getArgs()) .filter(arg -> arg instanceof Exception) .map(arg -> (Exception) arg) .findFirst(); if (exceptionOptional.isPresent() && RequestContextHolder.getRequestAttributes() != null) { Exception exception = exceptionOptional.get(); HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); log.error(ErrorLog.of(exception, request).toString()); } } }
- ๐ ์๋น์ค ์๊ฐ
- ๐จโ๐ฉโ๐งโ๐ฆ ๋ฐฉ๋ ์ ์ฃผ๋ฏผ ์๊ฐ
- ๐๏ธ 608๋ ๊ท์น
- ๐ฅ ๊น ์ปจ๋ฒค์
- ๐ค ์ฌ์ฉ์ ์๋๋ฆฌ์ค
- ๐ ๊ธฐ๋ฅ ๋ฆฌ์คํธ