Spring AOP After Throwing 을 이용한 Error log처리 (feat, AntPathMatcher)

떡ol·2022년 10월 9일
0

이전시간에 AOP에 대해서 알아봤다. 이번에는 자주사용하는 Advice인 After Throwing을 이용한 로그 처리를 배워보겠다.

1. AntPathMatcher로 매칭하기

우린 앞서 pointcut에 패키지명을 언급하므로써 aop를 작동하게할 시점에 대해서 알아봤다.

<aop:pointcut expression="execution(* com.service..*.*(..))" id="comAopMethod"/> 
<!-- com.service아래 페키지 전부실행한다는 뜻 -->

여기서 추가적으로 경로에 따라서도 한번 더 체크해줄려고한다. AntPathMatcher.match()매서드를 이용할껀데 설명은 다음과 같다.

  • ? : 1개의 문자와 매칭 (matches single character)
  • * : 0개 이상의 문자와 매칭 (matches zero or more characters)
  • ** : 0개 이상의 디렉토리와 파일 매칭 (matches all files / directories)

뭐, 사용 목적이나 방법이 단순해서 더 많은 정보를 원한다면 검색으로 찾아보자.

자바단은 다음과 같이 작성한다.

public class SimpleStackTrace {
	
	protected AntPathMatcher pathMatcher;
	protected String[] patterns;
	
	public AntPathMatcher getPathMatcher() {
		return pathMatcher;
	}

	public void setPathMatcher(AntPathMatcher pathMatcher) {
		this.pathMatcher = pathMatcher;
	}
	
	public String[] getPatterns() {
		return patterns;
	}
	
	public void setPatterns(String[] patterns) {
		this.patterns = patterns;
	}
}

요렇게 geter,seter 을 만들어두고 bean으로 주입해서 사용할 계획이다.

<bean id="simpleStackTrace" class="com.config.SimpleStackTrace">
		<property name="pathMatcher">
			<ref bean="antPathMatcher"/>
		</property>
		<property name="patterns">
			<list>
				<value>**.service.**</value>
			</list>
		</property>
</bean>
	
<bean id="antPathMatcher" class="org.springframework.util.AntPathMatcher"/>

antPathMatcher 라는 새로운 객체값을 만들어서 pathMatcher에 주입시키고
patterns에는 sevice단으로 관리되는 디렉토리에 전부와 매치해서 true값을 돌려준다.
(Main/service/.. , Login/service/.., Login/service/dao/.. 등등)

결국 AOP pointcut에 의해 패키지 값이 com.service이며, AntPathMacher에 의해 주소값(디랙토리)에 **/service/**로 둘다 되어있는 자료를 매칭한다.

그럼 이때 뭘 할거냐? e.printStackTrace()를 좀 간략하게 log에 출력해보자.

2.OutputStream 이용하여 printStackTrace() 출력문 짜집기하기

아래는 SimpleStackTrace.class에 추가되는 소스이다.

	//SimpleStackTrace.java -> public void setPatterns(String[] patterns) {} 아래에 추가

	public boolean run(Exception e, String canonicalName) {
		for (String pattern : patterns){
			if(pathMatcher.match(pattern, canonicalName)) {
				System.out.println(getMessage(e));
			}
		}
		return true;
	}
	
	public static String getMessage(Exception e) {
		return getSimpleStackTrace(e,5);
	}
	
	private static String getSimpleStackTrace(Exception e, int maxLines) {
		StringWriter sWriter = new StringWriter();
		PrintWriter pWriter = new PrintWriter(sWriter);
		e.printStackTrace(pWriter);
		String[] lines = sWriter.toString().split("\n");
		StringBuffer sb = new StringBuffer();
		
		for(int i=0; i< Math.min(lines.length, maxLines); i++) {
			sb.append(lines[i]).append("\n");
		}
		
		return sb.toString();
    }

현재 실행되는 소스 경로가 pattern 매칭되면, getSimpleStackTrace를 실행하며, 최종적으로 StringBuffer에 5줄까지만(이하, lines수 길이만큼만) 담아서 출력하게 해준다.

3. AOP Service 단과 연동하기

최종적으로 이전시간에 했던 AOP service에 주입해서 사용하면된다.

	<aop:config>
		<aop:pointcut expression="execution(* com.service..*.*(..))" id="comAopMethod"/>
		
		<aop:aspect ref="aopReference">
			<!-- 이전 학습자료
            <aop:before pointcut-ref="comAopMethod" method="before"/>
			<aop:after pointcut-ref= "comAopMethod" method="after"/>
			<aop:around pointcut-ref= "comAopMethod" method="around"/>
			<aop:after-throwing throwing="ex" pointcut-ref="comAopMethod" method="afterThrowing"/> -->
			<aop:after-throwing throwing="e" pointcut-ref="comAopMethod" method="SimpleStackTrace"/> 
		</aop:aspect>
	</aop:config>
	
	<bean id ="aopReference" class="com.config.AopReference">
		<property name="additionalAopService">
			<ref bean="simpleStackTrace"/>
		</property>
	</bean>
	
	<bean id="simpleStackTrace" class="com.config.SimpleStackTrace">
		<property name="pathMatcher">
			<ref bean="antPathMatcher"/>
		</property>
		<property name="patterns">
			<list>
				<value>**.service.**</value>
			</list>
		</property>
	</bean>
	
	<bean id="antPathMatcher" class="org.springframework.util.AntPathMatcher"/>

이렇게 작성해줬으면 com.config.AopReference에는 additionalAopService의 Setter이던, 객체던, 선언 되어있으야하므로,

public class AopReference {
	
	private static final Logger LOGGER = LoggerFactory.getLogger(AopReference.class);
	
	private SimpleStackTrace trace;
	
	/* 이전 학습자료
	 * public void before() { System.out.println("before AOP"); }
	 * public void after() { System.out.println("after AOP"); }
	 * public void afterThrowing(Exception ex) { System.out.println("aop send error:: " + ex); }
	 * public void around() { System.out.println("around AOP"); }
	 */
	
	public void SimpleStackTrace (JoinPoint joinPoint,Exception e) {
		Throwable throwable = e;
		getLog(joinPoint.getTarget().getClass()).error(e.getMessage());
		if(trace != null) {
			trace.run(e,joinPoint.getTarget().getClass().getCanonicalName());
		}
		
	}
	
	protected Logger getLog(Class<?> clazz) {
		return LoggerFactory.getLogger(clazz);
	}
	
	public void setAdditionalAopService(SimpleStackTrace trace) {
		this.trace = trace;
	}
	
}

System.out.print를 사용하기보단 logger를 이용하여 log를 출력하였다.
JoinPoint는 aspectj의 기능인데 실행된 시점에 Target의 정보를 가져올 수 있다.
Class의 정보가 필요하므로 이런식으로 사용하며, AntPathMatcher.match()로 비교 하기위한 주소 값은 Target에 포함되어있는 .getCanonicalName()를 넘겨 이용하면된다.

!결과

실행하면 다음과 같이 나온다. getLog() 한줄, trace.run() 최대 5줄까지 나와 보기 깔끔하다.



참고자료들___
(참고) Ant style pattern 정리

profile
하이

0개의 댓글