22.07.18 TIL + Log4j2 , AOP

Wintering·2022년 7월 18일
0

2022TIL

목록 보기
73/87

7월18일(월)

내일배움캠프 D+92

  • 프로젝트 마지막 주차! 내일 오전까지 완성을 하고, 팀원분들의 현업에 종사하는 지인 친구들에게 미리 피드백을 받아 볼 계획이어서 오늘 하루도 뭔가 여유없이 지나갔다. 사실 기능은 완성이 됐다고 생각했는데 자잘하게 빼먹은 기능이나, 사소하게 터지는 오류들이 많아서 해도해도 뭔가 끝나지않는다(?)라는 느낌이 들어서 조금 힘들었다.
    사실 어드민페이지에서 필터기능을 시간상 못 넣을 줄 알았는데, 이래저래 예상보다도 많은 범위에 페이징과 필터기능이 구현되어서 다행이었다. 스스로 맡은 파트에 대한 오류를 다 마치고 나서는 Log4j2로 오류를 찍어내는 로깅 작업을 했는데, 이해하는데 시간도 오래걸렸고 어려웠는데 막상 코드를 보니 엥? 이거 뿐인가? 라는 생각도 들고, 금새 잊을 것 같아서 정리해놓으려 한다!

Log4j2 & AOP

Today I Learned 📌

  • Log4j2
    • log4j(Log for Java)란 이전 버전인 Log4j 1.x에 비해 크게 개선된 Log4j로의 업그레이드이며 Logback 아키텍처의 몇 가지 고유한 문제를 수정하면서 Logback에서 사용할 수 있는 많은 개선 사항을 제공하는 대표적인 자바 로깅 프레임워크이다.
      og4j 2.13.0 이상에는 Java 8이 필요합니다. 버전 2.4~2.12.1에는 Java 7이 필요합니다(Log4j 팀은 더 이상 Java 7을 지원하지 않습니다). 일부 기능에는 선택적 종속성이 필요합니다. 이러한 기능에 대한 설명서는 필요한 종속성을 지정합니다.
  • AOP
    • AOP는 Aspect Oriented Programming 약자로 관점 지향 프로그래밍이라고 한다.
      반복되는 부분들이 흩어져 있는 것을 흩어진 관심사(Crosscutting Concerns) 라고 하는데, AOP는 흩어진 것을, 사진 아래 표시된 Aspect를 통해서 공통 로직이나 부가기능들을 분리하는 모듈화를 하고 어느 곳에서 사용할지 정의하여 관리할 수 있다.
      즉, 핵심 비즈니스 로직에서 분리하여 기존코드는 변경하지 않고 필요한 부분마다 재사용할 수 있게 한다.

Log4j2 사용하기

  1. 자바 스프링 부트의 기존 logback 종속성 제거하기

    dependencies {
    	implementation 'org.springframework.boot:spring-boot-starter-web'
    }
    configurations {
      all {
          exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging'
      }
    }
  2. Log4j2 프레임워크 종속성 추가하기

    dependencies {
    implementation 'org.springframework.boot:spring-boot-starter-log4j2'
    }
    ```
  3. Log4j2.xml 추가하기

    <?xml version="1.0" encoding="UTF-8" ?>
    <Configuration status="DEBUG">
     <Properties>
         <Property name="inputFileName">input</Property>
         <Property name="outputFileName">output</Property>
         <Property name="consoleLayout">%style{%d{yyyy/MM/dd HH:mm:ss,SSS}}{cyan} %highlight{[%-5p]}{FATAL=bg_red,
             ERROR=red,
             INFO=green, DEBUG=blue} [%C] %style{[%t]}{yellow}- %m%n -
         </Property>
         <Property name="fileLayout">[%d{yyyy-MM-dd HH:mm:ss}] [%c{1}] [%L] [%p] %m %n</Property>
     </Properties>
     <Appenders>
         <!-- console 패턴정의 -->
         <Console name="console" target="SYSTEM_OUT">
             <PatternLayout pattern="${consoleLayout}"></PatternLayout>
         </Console>
    
         <!-- input 로그를 기록하기 위한 파일 패턴 및 정책 정의 -->
         <RollingFile name="inputFile" fileName="logs/${inputFileName}.log"
                      filePattern="logs/${inputFileName}.%d{yyyy-MM-dd-hh}.log">
             <PatternLayout pattern="${fileLayout}"></PatternLayout>
             <Policies>
                 <TimeBasedTriggeringPolicy modulate="true" interval="1"></TimeBasedTriggeringPolicy><!-- 시간별 로그 파일 생성-->
             </Policies>
             <DefaultRolloverStrategy max="10" fileIndex="min"></DefaultRolloverStrategy>
         </RollingFile>
    
         <!-- output 로그를 기록하기 위한 파일 패턴 및 정책 정의 -->
         <RollingFile name="outputFile" fileName="logs/${outputFileName}.log"
                      filePattern="logs/${outputFileName}.%d{yyyy-MM-dd-hh}.log">
             <PatternLayout pattern="${fileLayout}"></PatternLayout>
             <Policies>
                 <TimeBasedTriggeringPolicy modulate="true"
                                            interval="24"></TimeBasedTriggeringPolicy><!-- 시간별 로그 파일 생성-->
             </Policies>
             <DefaultRolloverStrategy max="10" fileIndex="min"></DefaultRolloverStrategy>
         </RollingFile>
    
     </Appenders>
    
     <Loggers>
         <root level="info" additivity="false">
             <AppenderRef ref="console"></AppenderRef>
         </root>
    
         <!-- 스프링 프레임워크에서 찍는건 info / 콘솔만 설정 -->
         <logger name="org.springframework" level="info" additivity="false">
             <AppenderRef ref="console"/>
         </logger>
    
         <logger name="org.springframework" level="warn" additivity="false">
             <AppenderRef ref="inputFile"/>
             <AppenderRef ref="outputFile"/>
         </logger>
    
         <!-- input aspect 클래스 분리 / 콘솔과 input file 기록 -->
         <logger name="com.eventcafecloud.log4j.InputLogAspect" level="debug" additivity="false">
             <AppenderRef ref="console"></AppenderRef>
             <AppenderRef ref="inputFile"></AppenderRef>
         </logger>
    
         <!-- output aspect 클래스 분리 / 콘솔과 output file 기록 -->
         <logger name="com.eventcafecloud.log4j.OutputLogAspect" level="debug" additivity="false">
             <AppenderRef ref="console"></AppenderRef>
             <AppenderRef ref="outputFile"></AppenderRef>
         </logger>
     </Loggers>
    </Configuration>
    • Configuration
      Configuration은 설정 파일의 시작 태그를 나타낸다. status속성 값은 log4j2가 xml 설정 파일을 읽으면서 적용하는 단계에 출력할 log Level를 뜻한다.
    • Property
      Property는 말 그대로 해당 xml에서 사용할 속성 값을 정의하는 부분이다.
      뒤에 나올 Layout을 속성 값으로 정의해주었다.
    • Appender
      1. Console. : Console에 Log를 작성하는 Appender, 어떻게 작성할지 패턴을 명시해준다.
      2. RollingFile. : File에 Log를 작성하는 Appender, 파일의 이름, 파일명의 패턴, Log내용을 어떻게 작성할지에 대한 패턴, 파일을 어떻게 Rolling(회전)시킬지에 대한 정책(size?, TimeRange?.....), 파일 삭제 정책 등을 명시해준다.

AOP 사용하기

위의 Log4j2.xml 코드에서 com.eventcafecloud.log4j.OutputLogAspect, com.eventcafecloud.log4j.OutputLogAspect 이 두 가지가 AOP를 사용한 부분이다.

현재 내가 진행하는 프로젝트는 글로벌 예외처리가 되어 있다.

@Controller
public class Error implements ErrorController {
    private final String ERROR_TEMPLATES_PATH = "/error/";

    @RequestMapping(value = "/error")
    public String handleError(HttpServletRequest request, User loginUser) {
        Object status = request.getAttribute(RequestDispatcher.ERROR_STATUS_CODE);
        if (status != null) {
            int statusCode = Integer.valueOf(status.toString());
            if (statusCode == HttpStatus.UNAUTHORIZED.value()) {
                return ERROR_TEMPLATES_PATH + "401";
            }
            if (statusCode == HttpStatus.FORBIDDEN.value()) {
                return ERROR_TEMPLATES_PATH + "403";
            }
            if (statusCode == HttpStatus.NOT_FOUND.value()) {
                return ERROR_TEMPLATES_PATH + "404";
            }
            if (statusCode == HttpStatus.METHOD_NOT_ALLOWED.value()) {
                System.out.println("실행");
                return ERROR_TEMPLATES_PATH + "405";
            }

            if(statusCode == HttpStatus.INTERNAL_SERVER_ERROR.value()){
                return ERROR_TEMPLATES_PATH + "500";
            }
        }
        return "error";
    }
}

0개의 댓글