π 2024λ 01μ 29μΌ
- application.yml
# trace < debug < info < error μμΌλ‘ λ‘κ·Έλ 벨λ₯Ό κ°μ§ # info μ€μ μ trace, debug λ‘κ·Έλ μΆλ ₯λμ§ μμ logging: # λ‘κ·ΈνμΌ κ²½λ‘μ νμΌλͺ μ§μ file: name: logs/app.log level: root: info
- LogTestController
/* @Slf4j λ₯Ό ν΅ν΄ logBack λ‘κ·ΈλΌμ΄λΈλ¬λ¦¬ μ¬μ©κ°λ₯ */ @Slf4j @RestController public class LogTestController { /* @Slf4jλ₯Ό μ¬μ©νμ§ μκ³ , μ§μ λΌμ΄λΈλ¬λ¦¬ importμ λ‘κ±° μμ±κ°λ₯ */ // private static final Logger loger = LoggerFactory.getLogger("logger"); @GetMapping("/log/test1") public String testMethod1(){ log.debug("λλ²κ·Έ λ‘κ·Έμ λλ€."); log.info("μΈν¬ λ‘κ·Έμ λλ€."); log.error("μλ¬ λ‘κ·Έμ λλ€."); return "ok"; } }
- λ‘κΉ μ¬μ© μ΄μ
λͺ©μ μ€λͺ λλ²κΉ λ° λ¬Έμ ν΄κ²° μ΄ν리μΌμ΄μ μ λ¬Έμ λ₯Ό μλ³νκ³ ν΄κ²°νκΈ° μν΄ λ‘κ·Έλ₯Ό μ¬μ©. λ³μ κ°, λ©μλ νΈμΆ, μμΈ μ€ν νΈλ μ΄μ€ λ±μ΄ κΈ°λ‘λ¨. μ±λ₯ λͺ¨λν°λ§ μ΄ν리μΌμ΄μ μ μ±λ₯μ μΆμ νκ³ λͺ¨λν°λ§νκΈ° μν΄ λ‘κ·Έλ₯Ό μ¬μ©. λ©μλ μ€ν μκ°, λ°μ΄ν°λ² μ΄μ€ 쿼리 μμ μκ° λ±μ΄ κΈ°λ‘λ μ μμ. κ°μ¬ λ° λ³΄μ μ¬μ©μμ νλ μΆμ , 보μ μ΄λ²€νΈ λ‘κΉ λ±μ ν΅ν΄ κ°μ¬ λ° λ³΄μ λͺ©μ μΌλ‘ μ¬μ©λ¨. μ΄μ λ° λͺ¨λν°λ§ μ΄ν리μΌμ΄μ μ μ΄μ μνλ₯Ό μΆμ νκ³ λͺ¨λν°λ§νκΈ° μν΄ λ‘κ·Έλ₯Ό μ¬μ©. μ€μ μ§μ€μμΌλ‘ λͺ¨λν°λ§λ μ μμ.
- log νμΌ κ΄λ¦¬(logback-spring.xml)
<?xml version="1.0" encoding="UTF-8"?> <configuration> <!--CONSOLE λ‘κ·Έ κ΄λ ¨ μ€μ --> <appender name = "CONSOLE" class ="ch.qos.logback.core.ConsoleAppender"> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss} %logger{36} - %msg%n </pattern> </encoder> </appender> <!--μ μλ‘κ·Έ κ΄λ ¨ μ€μ --> <!--λ‘κΉ μμ rolling μ΄λΌλ κ²μ λ μ§μ λ³ν¨μ λ°λΌ νμΌμ μλ‘κ² λ§λ€μ΄μ£Όλ κ² --> <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <file>logs/app.log</file> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss} %logger{36} - %msg%n </pattern> </encoder> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>logs/app.%d{yyyy-MM-dd HH:mm:ss}.log</fileNamePattern> <maxHistory>30</maxHistory> </rollingPolicy> </appender> <!--μλ¬λ‘κ·Έ κ΄λ ¨ μ€μ --> <appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender"> <filter class="ch.qos.logback.classic.filter.LevelFilter"> <level>error</level> <onMatch>ACCEPT</onMatch> <onMismatch>DENY</onMismatch> </filter> <file>logs/app-error.log</file> <encoder> <pattern>%d{yyyy-MM-dd HH:mm:ss} %logger{36} - %msg%n </pattern> </encoder> <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy"> <fileNamePattern>logs/app-error.%d{yyyy-MM-dd HH:mm:ss}.log</fileNamePattern> <maxHistory>30</maxHistory> </rollingPolicy> </appender> <!--info λ‘κ·Έμ κ²½μ° μ μ logνμΌλ‘ λΆλ¦¬--> <root level = "info"> <!--console κ³Ό μ μ λ‘κ·ΈνμΌλ λ‘κ·Έλ΄μ© μΆκ°--> <appender-ref ref="CONSOLE"/> <appender-ref ref="FILE"/> <appender-ref ref="ERROR_FILE"/> </root> <!--error λ‘κ·Έμ κ²½μ° μλ¬ log νμΌλ‘ λΆλ¦¬--> <!-- <logger level = "error" name="com.encore.board">--> <!-- <appender-ref ref="ERROR_FILE"/>--> <!-- </logger>--> </configuration>
μ©μ΄/κ°λ λ΄μ© Aspect (κ΄μ ) ν΅μ¬ λΉμ¦λμ€ λ‘μ§μμ λ°μνλ ν‘λ¨ κ΄μ¬μ¬λ₯Ό λͺ¨λνν κ²μΌλ‘, μλ₯Ό λ€μ΄ λ‘κΉ , νΈλμμ μ²λ¦¬, 보μ λ±μ΄ ν΄λΉλ¨. Advice (μ‘°μΈ) μ΄λ μμ μμ μ΄λμ κ΄μ μ μ μ©ν μ§λ₯Ό μ μν κ²μΌλ‘, λ©μλ νΈμΆ μ , ν, μμΈ λ°μ μ λ±κ³Ό κ°μ μ§μ μμ μ μ©λ μ μμ. Pointcut (ν¬μΈνΈμ»·) μ΄λ€ μ§μ μμ μ΄λ€ κ΄μ μ μ μ©ν μ§λ₯Ό μ μν κ²μΌλ‘, λ©μλ νΈμΆ, κ°μ²΄ μμ±, νλ μ κ·Ό λ± λ€μν μ§μ μ μ§μ ν μ μμ. Join Point (μ‘°μΈ ν¬μΈνΈ) νλ‘κ·Έλ¨ μ€ν μ€ νΉμ μμ μΌλ‘, κ΄μ μ΄ μ μ©λμ΄μΌ νλ νΉμ μ§μ μ λνλ. λ©μλ νΈμΆ, κ°μ²΄ μμ± λ±μ΄ μ‘°μΈ ν¬μΈνΈμ ν΄λΉν μ μμ. Weaving (μλΉ) κ΄μ μ ν΅μ¬ λΉμ¦λμ€ λ‘μ§μ μ μ©νλ κ³Όμ μΌλ‘, μ½λμ κ΄μ μ μ½μ νκ±°λ μ°κ²°νμ¬ μ€νλλλ‘ λ§λλ κ²μ μλ―Έν¨.
- AopLogService
@Slf4j @Aspect @Component public class AopLogService { /* aop μ λμμ΄ λλ controller, service λ±μ μ μ @Pointcut("execution(* com.encore.board..controller..*.*(..))") */ @Pointcut("within(@org.springframework.stereotype.Controller *)") public void controllerPointCut(){ } // λ°©μ 1 @Before("controllerPointCut()") public void beforeController(JoinPoint joinPoint){ log.info("Before Controller"); /* λ©μλκ° μ€νλκΈ° μ μ μΈμ¦, μ λ ₯κ° κ²μ¦λ±μ μννλ μ©λλ‘ μ¬μ©νλ μ¬μ λ¨κ³ */ HttpServletRequest httpServletRequest = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); /* json ννλ‘ μ¬μ©μμ μμ²μ 쑰립νκΈ° μν λ‘μ§ */ ObjectMapper objectMapper = new ObjectMapper(); ObjectNode objectNode = objectMapper.createObjectNode(); objectNode.put("Method Name",joinPoint.getSignature().getName()); objectNode.put("CRUD Name", httpServletRequest.getMethod()); Map<String, String[]> paramMap = httpServletRequest.getParameterMap(); ObjectNode objectNodeDetail = objectMapper.valueToTree(paramMap); objectNode.set("user inputs", objectNodeDetail); log.info("user request info" + objectNode); } @After("controllerPointCut()") public void afterController(){ log.info("end Controller"); } // λ°©μ 2 @Around("controllerPointCut()") public Object controllerLogger(ProceedingJoinPoint proceedingJoinPoint){ // joinPoint λ aop λμμΌλ‘ νλ 컨νΈλ‘€λ¬μ νΉμ λ©μλλ₯Ό μλ―Έ // log.info("request method"+ proceedingJoinPoint.getSignature().toString()); /* μ¬μ©μμ μμ²κ°μ json ννλ‘ μΆλ ₯νκΈ° μν΄ HttpServletRequest κ°μ²΄λ₯Ό κΊΌλ΄λ λ‘μ§ */ HttpServletRequest httpServletRequest = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); /* json ννλ‘ μ¬μ©μμ μμ²μ 쑰립νκΈ° μν λ‘μ§ */ ObjectMapper objectMapper = new ObjectMapper(); ObjectNode objectNode = objectMapper.createObjectNode(); objectNode.put("Method Name",proceedingJoinPoint.getSignature().getName()); objectNode.put("CRUD Name", httpServletRequest.getMethod()); Map<String, String[]> paramMap = httpServletRequest.getParameterMap(); ObjectNode objectNodeDetail = objectMapper.valueToTree(paramMap); objectNode.set("user inputs", objectNodeDetail); log.info("user request info" + objectNode); try { return proceedingJoinPoint.proceed(); }catch (Throwable e){ log.error(e.getMessage()); throw new RuntimeException(e); } } }
μ΄λ Έν μ΄μ μ°μμΈ λ΄μ© @Pointcut Pointcutμ μ μνλ λ° μ¬μ© νΉμ μ§μ (λ©μλ νΈμΆ, κ°μ²΄ μμ± λ±)μ AOPλ₯Ό μ μ©νκΈ° μν΄ μ¬μ©λλ ννμμ μ μν©λλ€. ν΄λΉ ννμμ μ¬λ¬ μ΄λλ°μ΄μ€μμ 곡μ λ μ μμ΅λλ€. @Before λ©μλ μ€ν μ μ μ΄λλ°μ΄μ€λ₯Ό μ€ν μ΄λλ°μ΄μ€ λ©μλλ₯Ό νΉμ μ‘°μΈ ν¬μΈνΈ(λ©μλ νΈμΆ λ±) μ μ μ€νν©λλ€. μ£Όλ‘ μ μ²λ¦¬ λ‘μ§μ μννλ λ° μ¬μ©λ©λλ€. @After λ©μλ μ€ν νμ μ΄λλ°μ΄μ€λ₯Ό μ€ν μ΄λλ°μ΄μ€ λ©μλλ₯Ό νΉμ μ‘°μΈ ν¬μΈνΈ νμ μ€νν©λλ€. μ£Όλ‘ νμ²λ¦¬ λ‘μ§μ μννκ±°λ μμΈ μ²λ¦¬μ μ¬μ©λ©λλ€. @Around λ©μλ μ€ν μ νμ μ΄λλ°μ΄μ€λ₯Ό μ€ν μ΄λλ°μ΄μ€ λ©μλκ° μ‘°μΈ ν¬μΈνΈλ₯Ό κ°μΈμ μ€ν μ νμ λ‘μ§μ μνν μ μμ΅λλ€. κ°μ₯ μ μ°ν μ΄λλ°μ΄μ€λ‘, μ , ν μ²λ¦¬ λ° μμΈ μ²λ¦¬ λ± λͺ¨λ λ‘μ§μ ν¬ν¨ν μ μμ΅λλ€.
: 컨νΈλ‘€λ¬ νΉνκΈ°λ₯ μ£Όλ‘ μμΈμ²λ¦¬
- CommonException
@Slf4j @ControllerAdvice public class CommonException { @ExceptionHandler(EntityNotFoundException.class) public ResponseEntity<Map<String, Object>> entityNotFoundExceptionHandler(EntityNotFoundException e){ log.error("EntityNotFoundException message : " + e.getMessage()); return this.responseErrorMassage(HttpStatus.NOT_FOUND,e.getMessage()); } @ExceptionHandler(IllegalArgumentException.class) public ResponseEntity<Map<String, Object>> IllegalArgumentExceptionHandler(IllegalArgumentException e){ log.error("IllegalArgumentException : " + e.getMessage()); return this.responseErrorMassage(HttpStatus.BAD_REQUEST,e.getMessage()); } private ResponseEntity<Map<String, Object>> responseMassage(HttpStatus status, Object object){ Map<String, Object> map = new HashMap<>(); map.put("status", Integer.toString(status.value())); map.put("message", object); return new ResponseEntity<>(map, status); } private ResponseEntity<Map<String, Object>> responseErrorMassage(HttpStatus status, String message){ Map<String, Object> map = new HashMap<>(); map.put("status", Integer.toString(status.value())); map.put("status message", status.getReasonPhrase()); map.put("error message", message); return new ResponseEntity<>(map, status); } }
λ§μ½ 컨νΈλ‘€λ¬μμ try-catch λΈλ‘ λ΄μμ μμΈλ₯Ό μ²λ¦¬νλ©΄ ν΄λΉ λ©μλμ μμΈ μ²λ¦¬ λ‘μ§μ΄ μ°μ νΈμΆλκ³ , μ²λ¦¬νμ§ μκ±°λ μ νλλ κ²½μ°μ @ControllerAdviceμ @ExceptionHandler λ©μλκ° νΈμΆ