Skip to content

โœ๏ธ BE LOGGING

gmuz1c edited this page Aug 8, 2024 · 2 revisions

๋กœ๊ทธ ์ „๋žต

ERROR โ†’ RuntimeException ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ์„œ๋ฒ„ ์—๋Ÿฌ๋ฅผ handler ํ•จ์ˆ˜ ์‹คํ–‰ ์ „ ๊ธฐ๋กํ•œ๋‹ค

WARN โ†’ BanggoodException, MethodArgumenthotValidException, OauthException ๋“ฑ ์‚ฌ์šฉ์ž ์ž…๋ ฅ ์—๋Ÿฌ๋ฅผ ํฌํ•จํ•œ ์„œ๋ฒ„์—์„œ ์ฝ”๋“œ์ƒ์œผ๋กœ ์˜ˆ์™ธ ์ฒ˜๋ฆฌ๋ฅผ ํ•œ ์—๋Ÿฌ๋ฅผ handler ํ•จ์ˆ˜ ์‹คํ–‰ ์ „ ๊ธฐ๋กํ•œ๋‹ค

INFO โ†’ Service ํด๋ž˜์Šค ์•ˆ Public ๋ฉ”์„œ๋“œ ์ž‘์—… ์™„๋ฃŒ ์‹œ์ , ์ฆ‰ ํ•จ์ˆ˜๊ฐ€ ๋๋‚˜๋Š” ์‹œ์ ์— ๊ธฐ๋กํ•œ๋‹ค

โ‡’ ๊ฐ€์žฅ ๋ณดํŽธ์ ์ธ ๋กœ๊ทธ ์ „๋žต์„ ์ˆ˜๋ฆฝํ•˜์˜€๋‹ค ์ถ”ํ›„ ์ถ”๊ฐ€ํ•˜๊ฑฐ๋‚˜ ํ•„์š” ์—†๋Š” ๋กœ๊ทธ๊ฐ€ ์žˆ์„ ๊ฒฝ์šฐ ๋ณ€๊ฒฝํ•œ๋‹ค


๋กœ๊ทธ ํ…œํ”Œ๋ฆฟ

BASE

  • ์š”์ฒญ ์‹œ๊ฐ„(timestamp)
  • ๋กœ๊ทธ ์‹๋ณ„์ž (UUID)
  • ์š”์ฒญ URL (type ํฌํ•จ)
public abstract class BaseLog {

    private final LocalDateTime requestTime;
    private final String requestUrl;
    private final String uuid;

ERROR

  • ์—๋Ÿฌ ๋ฉ”์‹œ์ง€
  • stackTrace
public class ErrorLog extends BaseLog {

    private final String errorMessage;
    private final String stackTrace;

WARN

  • ๊ฒฝ๊ณ  ๋ฉ”์‹œ์ง€
public class WarnLog extends BaseLog {

    private final String warnMessage;

INFO

  • ํ˜ธ์ถœ ๋ฉ”์„œ๋“œ ์ด๋ฆ„
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());
            }
        }
    }
Clone this wiki locally