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

떡ol·2022년 10월 9일

이전시간에 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개의 댓글