이전시간에 AOP에 대해서 알아봤다. 이번에는 자주사용하는 Advice인 After Throwing을 이용한 로그 처리를 배워보겠다.
우린 앞서 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에 출력해보자.
아래는 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수 길이만큼만) 담아서 출력하게 해준다.
최종적으로 이전시간에 했던 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 정리