본문 바로가기
Java Web Programming/6. Spring | MyBatis

[Spring] Logging 로깅 - Log4j (스프링 AOP 방식 적용)

by 파프리카_ 2020. 10. 28.
728x90
반응형

[요구사항]

현재 시스템에서 서비스 중인 package - org.kosta.model 이하의 Service 계열의 class의 메서드를 대상으로,

예외 발생 여부와 관계 없이 어떤 서비스(메서드 실행)가 실행되는 지를 

특정 파일(report.log)에 시간 정보와 클래스와 메서드 정보를 로깅하도록 한다.

 

파일 입출력 (File IO) 관련 오픈 소스 라이브러리를 이용해보자! → 자바 Logging Library인 Log4j를 이용한다.

*별도의 프로젝트에서 테스트 후 적용해보도록 한다.


log4j: 프로그램 작성시 로그를 남기기 위해 사용되는 자바 기반 로깅 라이브러리

설정 파일에서 팩키지별로 레벨 지정이 가능, 지정한 등급 이상의 로그만 저장하는 방식이다.

FATAL(가장 높은 로그레벨)

ERROR

WARN

INFO

DEBUG

TRACE(가장 낮은 로그레벨)



[ 적용 및 개발 단계]

1. maven pom.xml에 log4j lib 추가

2. 각 class의 메서드에 log4j 로깅을 info 레벨로 처리

3. MemberSerivceImpl에 log4j를 이용해, 각 메서드에 log 추가

 

But! 위 처럼한다면, 로깅 대상 클래스의 메서드 수가 많을 수록 동일한 작업이 반복되어 비효율적이다.

 

- AOP를 적용하여 해결해보자

1. maven pom.xml  : aop lib 추가

1. 횡단관심사항(cross cutting concern)을 정의 : aop.common.LoggingService

3. spring-config.xml에서 aop설정

- Namespaces : aop 체크

- pointcut으로 적용대상을 지정

- advice로 before를 주어, 메서드 싱행 전에 로깅이 되도록 적용시점을 지정

4. log4j2.xml을 설정


/pom.xml

<dependencies>
  	<!-- Spring Library -->
	<dependency>
	    <groupId>org.springframework</groupId>
	    <artifactId>spring-context</artifactId>
	    <version>4.3.14.RELEASE</version>
	</dependency>
	
	<!-- Log4j 라이브러리 -->
	 <dependency>
	    <groupId>org.apache.logging.log4j</groupId>
	    <artifactId>log4j-core</artifactId>
	    <version>2.12.1</version>
	 </dependency>
	 
	<!-- AOP Library -->
	<dependency>
	    <groupId>org.aspectj</groupId>
	    <artifactId>aspectjweaver</artifactId>
	    <version>1.8.1</version>
	</dependency>
</dependencies>

 

/log4j.xml

<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
    <Appenders>        
        <Console name="console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d %-5p [%t] %C{10} (%F:%L) - %m%n" />
        </Console>
        <File name="file" fileName="report.log" append="true">
            <PatternLayout pattern="%d %-5p [%t] %C{10} (%F:%L) - %m%n" />
        </File>
    </Appenders>

    <Loggers>
        <Root level="warn">
            <AppenderRef ref="console" />
        </Root>	
        <!-- aop.common 하위의 패키지 이하 클래스들만 별도로 Logging 설정 
             : spring-config.xml에서 다른 Service들이 loggingService를 aop하고 있으므로!
        	 - level은 info 이상 로깅되도록 하고
        	 - root level의 로깅 정책을 이어받지 않는다는 설정을 한다.
        	   (additivity = "false")
        -->	
        <Logger name="aop.common" level="info" additivity="false">
        	<AppenderRef ref="file"/>
        </Logger>
    </Loggers>
</Configuration>

/MemberService.java <<interface>>

package org.kosta.model.member;

public interface MemberService {
	void findMemberById();
	void findMemberListByAddress();
	void registerMember();
}

 

/MemberServiceImpl.java 

- core 핵심 사항 : findMemberById / findMemberListByAddress / registerMember

- cross-cutting concern : log

package org.kosta.model.member;

public class MemberServiceImpl implements MemberService {
	@Override
	public void findMemberById() {
		System.out.println("아이디로 회원 검색");
	}
	
	@Override
	public void findMemberListByAddress() {
		System.out.println("주소로 회원리스트 검색");
	}
	
	@Override
	public void registerMember() {
		System.out.println("회원가입");
	}
	
}

/ProductService.java <<interface>>

package org.kosta.model.product;

public interface ProductService {
	void registerProduct();
	void findProductById();
	void findProductListByMaker();
}

 

/ProductServiceImpl.java

- core 핵심 사항 : registerProduct / findProductById / findProductListByMaker

- cross-cutting concern : log

package org.kosta.model.product;

public class ProductServiceImpl implements ProductService {
	@Override
	public void registerProduct() {
		System.out.println("상품 등록");
	}
	
	@Override
	public void findProductById() {
		System.out.println("아이디로 상품 검색");
	}
	
	@Override
	public void findProductListByMaker() {
		System.out.println("제조사로 상품리스트 검색");
	}
}

횡단 관심 사항을 정의하는 클래스 - AOP 적용!
(Logging : 대상 메서드가 실행완료 전(advice-before로 설정됨), 시간 정보를 특정 공간에 기록하는 서비스)

 

/LoggingService.java

package aop.common;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.aspectj.lang.JoinPoint;
/*
 * 횡단 관심사항을 정의한 클래스
 */
public class LoggingService {
	//Log4j를 사용하기 위한 선언부
	Logger log = LogManager.getLogger(LoggingService.class);

	public void beforeLogging(JoinPoint point) {
		//횡단 관심 사항을 적용할 대상 class의 이름
		String className = point.getTarget().getClass().getName();
		//횡단 관심 사항을 적용할 대상 메서드의 이름
		String methodName = point.getSignature().getName();
		
		//System.out.println(className+" "+methodName);
		log.info(className + " "+ methodName);
	}
}

횡단 관심 사항 서비스 정의한 bean + AOP 적용 

1. 횡단 관심 사항을 정의 = Logging

2. IOC 기반 하에, AOP 설정을 pointcut으로 적용대상을 지정

3. advice로 횡단괌심사항 서비스 적용 시점을 지정한다.

 

/spring-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
	
	<bean id="memberService" class="org.kosta.model.member.MemberServiceImpl"></bean>
	<bean id="productService" class="org.kosta.model.product.ProductServiceImpl"></bean>
	
	<!-- 횡단 관심 사항 서비스를 정의한 bean -->
	<bean id="loggingService" class="aop.common.LoggingService"></bean>
	
	<!-- AOP 설정 -->
	<aop:config>
		<aop:aspect ref="loggingService">
			<!--  pointcut : 적용대상을 지정
				- public : 메서드 접근 제어자
				- * : 모든 리턴타입이 대상
				- org.kosta.model.. : org.kosta.model의 하위 패키지의 하위 패키지까지 모두 선택
				- *Service : Service로 끝나는 class
				- * : 그 안에있는 모든 메서드 명
				- (..) : 모든 매개변수가 대상이 된다 (int, String, 0 등등..)-->
			<!-- execution([접근제어자] [returnType].[package]..[className].[method].([매개변수리스트]) -->
			<aop:pointcut expression="execution(public * org.kosta.model..*Service.*(..))" id="pt"/>
			<aop:before method="beforeLogging" pointcut-ref="pt"/>
		</aop:aspect>
	</aop:config>
</beans>

/TestService.java

package test;

import org.kosta.model.member.MemberService;
import org.kosta.model.product.ProductService;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestService {
	public static void main(String[] args) {
		ClassPathXmlApplicationContext factory
			= new ClassPathXmlApplicationContext("spring-config.xml");
		
		MemberService ms = (MemberService) factory.getBean("memberService");
		ms.findMemberById();
		ms.findMemberListByAddress();
		ms.registerMember();
		
		System.out.println("------------------------------");
		
		ProductService ps = (ProductService) factory.getBean("productService");
		ps.findProductById();
		ps.findProductListByMaker();
		ps.registerProduct();
		
		factory.close();
	}
}

[ 결과 ]

- console

 

- report.log : TestService.java를 실행할때마다 로그가 쌓인다

 

728x90
반응형