前段时间,项目中讨论了改进logging的问题。
我们在日常的代码中常常需要在在一个方法(method)的开始的时候log一下输入参数,在方法(method)结束时log一下return参数(很常见的问题),之前我们都是通过开发人员自己手动写代码去实现log,但是这样真的很麻烦,很多类似log的代码在程序里也不好看,于是借鉴了他人的想法,利用反射和method级的annotation来实现对入参和return值的logging。
1。首先定义一个Annotation来标记method是否需要打log
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MethodLogEnabler {
/**
* log flag
*/
public static enum LOG {
/**
* log input parameters
*/
IN,
/**
* log output parameters
*/
OUT,
/**
* log both input & output parameters
*/
IN_OUT}
/**
* The log value
* @return The log value
*/
public LOG value();
}
2。实现一个写log的Spring拦截器
public class MethodLoggerInterceptor implements MethodInterceptor{
public Object invoke(MethodInvocation arg0) throws Throwable {
MethodLoggerHandler handler = new MethodLoggerHandler();
MyLogger logger = new MyLogger();
handler.setLogger(logger);
return handler.execute(arg0);
}
}
拦截器所调用的handler类
/**
* To check if the invoking method is registered MethodLogEnabler
* annotation, and log invoking method's parameter if the annotatioin
* is registered
*/
public class MethodLoggerHandler {
/**
* Default Constructor
*/
public MethodLoggerHandler(){
//Default Constructor
}
/**
*
* @param methodInvocation
* @return Object
* @throws Throwable
*/
public Object execute(MethodInvocation methodInvocation) throws Throwable {
Method method = methodInvocation.getMethod();
MethodLogEnabler annotation = null;
//Check if annotation exists
if (method != null && method.getAnnotation(MethodLogEnabler.class) != null) {
annotation = method.getAnnotation(MethodLogEnabler.class);
}
//try to log method's input parameter
if (method != null && annotation != null) {
logInputParameters(methodInvocation, method, annotation);
}
Object result = null;
try {
//the original method call
result = methodInvocation.proceed();
} catch (Throwable e) {
//log the error.
if (logger != null) {
logger.error("", e);
}
throw e;
}
//try to log method's return values
if (method != null && annotation != null) {
logOutputValues(method, annotation, result);
}
return result;
}
private void append(Object obj, StringBuilder appender) {
if (obj != null) {
//handle array object
if (obj.getClass().isArray()) {
Object[] objects = (Object[]) obj;
appender.append(getArrayOutput(objects));
}
//handle list object
if (obj instanceof List) {
Object[] objects = toArray(obj);
appender.append(getArrayOutput(objects));
}
appender.append(obj.toString());
}
}
@SuppressWarnings("unchecked")
private Object[] toArray(Object obj) {
Object[] objects = ((List) obj).toArray();
return objects;
}
private String getArrayOutput(Object[] objects) {
if ((objects != null) && (objects.length > 0)) {
StringBuffer arrayOutput = new StringBuffer();
arrayOutput.append("ARRAY[");
for (Object object : objects) {
if (object != null) {
arrayOutput.append(object.toString()).append(",");
}
}
// remove last ,
if (arrayOutput.length() > 0) {
arrayOutput.replace(arrayOutput.length() - 1, arrayOutput.length(), "");
}
arrayOutput.append("]");
return arrayOutput.toString();
}
return null;
}
private void logOutputValues(Method method, MethodLogEnabler annotation, Object result) {
if (MethodLogEnabler.LOG.OUT.equals(annotation.value())
|| MethodLogEnabler.LOG.IN_OUT.equals(annotation.value())) {
//the method has return
if (!Void.TYPE.equals(method.getReturnType())) {
StringBuilder output = new StringBuilder();
output.append("Class[").append(method.getDeclaringClass().getName()).append("]: Method[")
.append(method.getName()).append("]: Retrun Value[");
append(result, output);
output.append("]");
if (logger != null && logger.isDebugEnabled()) {
logger.debug(output.toString());
}
}
}
}
private void logInputParameters(MethodInvocation methodInvocation, Method method, MethodLogEnabler annotation) {
if (MethodLogEnabler.LOG.IN.equals(annotation.value())
|| MethodLogEnabler.LOG.IN_OUT.equals(annotation.value())) {
//the method has input parameters
if (method.getParameterTypes().length > 0) {
StringBuilder string = new StringBuilder();
string.append("Class[").append(method.getDeclaringClass().getName()).append("]: Method[").append(
method.getName()).append("]: Parameter[");
for (Object input : methodInvocation.getArguments()) {
if (logger != null && logger.isDebugEnabled()) {
append(input, string);
string.append(",");
}
}
string.append("]");
if (logger != null && logger.isDebugEnabled()) {
logger.debug(string.toString());
}
}
}
}
/**
* @param logger The logger to set.
*/
public void setLogger(Logger logger) {
this.logger = logger;
}
/**
* @return Returns the logger.
*/
public Logger getLogger() {
return logger;
}
private Logger logger;
}
3。Junit的测试
写一个DumpDTO
/**
* class DumpDTO
*/
public class DumpDTO {
private String id;
private int num;
private Date time;
private Long u;
/**
* @return Returns the id.
*/
public String getId() {
return id;
}
/**
* @param id The id to set.
*/
public void setId(String id) {
this.id = id;
}
/**
* @return Returns the num.
*/
public int getNum() {
return num;
}
/**
* @param num The num to set.
*/
public void setNum(int num) {
this.num = num;
}
/**
* @return Returns the time.
*/
public Date getTime() {
return time;
}
/**
* @param time The time to set.
*/
public void setTime(Date time) {
this.time = time;
}
/**
* @return Returns the u.
*/
public Long getU() {
return u;
}
/**
* @param u The u to set.
*/
public void setU(Long u) {
this.u = u;
}
}
建一个DumpHandler接口,接口里使用刚创建的MethodLogEnabler标签去定义哪些方法需要打log
public interface DumpHandler {
/**
* no log, because no input paramters
*/
@MethodLogEnabler(MethodLogEnabler.LOG.IN)
public void doA();
/**
*
* @param a
* @param b
* @param c
* @param d
*/
@MethodLogEnabler(MethodLogEnabler.LOG.IN)
public void doB(String a, int b, Date c, Long d);
/**
* Only log return values
* @param dto
* @param string
* @return DumpDTO
*/
@MethodLogEnabler(MethodLogEnabler.LOG.OUT)
public DumpDTO doC(DumpDTO dto, String string);
/**
* Log both input parameters and return values
* @param dto
* @return List<DumpDTO>
*/
@MethodLogEnabler(MethodLogEnabler.LOG.IN_OUT)
public List<DumpDTO> doD(DumpDTO[] dto);
}
简单的实现Handler的接口
public class DumpHandlerImpl implements DumpHandler {
/* (non-Javadoc)
* @see se.ericsson.nrg.ws.adapter.bwlist.DumpHandler#doA()
*/
public void doA() {
System.out.println("doA");
}
/* (non-Javadoc)
* @see se.ericsson.nrg.ws.adapter.bwlist.DumpHandler#doB(java.lang.String, int, java.util.Date, java.lang.Long)
*/
@SuppressWarnings("unused")
public void doB( String a, int b, Date c, Long d) {
System.out.println("doB");
}
/* (non-Javadoc)
* @see se.ericsson.nrg.ws.adapter.bwlist.DumpHandler#doC(se.ericsson.nrg.ws.adapter.bwlist.DumpDTO, java.lang.String)
*/
public DumpDTO doC(DumpDTO dto, String string) {
System.out.println("doC");
dto.setId("updateId: " + string);
return dto;
}
/* (non-Javadoc)
* @see se.ericsson.nrg.ws.adapter.bwlist.DumpHandler#doC(se.ericsson.nrg.ws.adapter.bwlist.DumpDTO[])
*/
public List<DumpDTO> doD(DumpDTO[] dto) {
System.out.println("doD");
return Arrays.asList(dto);
}
}
Junit的代码
public class MethodLoggerHandlerTest extends TestCase {
private static XmlBeanFactory factory;
/* (non-Javadoc)
* @see junit.framework.TestCase#setUp()
*/
@Override
protected void setUp() throws Exception {
super.setUp();
File file = new File("test/junit/applicationContext.xml");
if(file.exists()){
System.out.println("file exits");
}
factory = new XmlBeanFactory(new FileSystemResource("test/junit/applicationContext.xml"));
}
/**
* unit test
*/
public void testExecute() {
DumpHandler handler = (DumpHandler)factory.getBean("dumpHandler");
DumpDTO[] dtos = new DumpDTO[3];
for(int i = 0; i < dtos.length; i++) {
DumpDTO dto = new DumpDTO();
dto.setId("id" + i);
dto.setNum(100 + i);
dto.setTime(new Date());
dto.setU(1000l + i);
dtos[i] = dto;
}
handler.doA();//no log
handler.doB("a", 100, new Date(), 1000l); //log input
handler.doC(dtos[0], getName()); // log return only, see annotation defined in DumpHandler
handler.doD(dtos); // log both input and return
}
}
最后是Spring的applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans>
<bean id="dumpHandler"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="proxyInterfaces">
<list>
<value>xxx.xxx.DumpHandler</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>logInterceptor</value>
</list>
</property>
<property name="target">
<ref bean="testImpl" />
</property>
</bean>
<bean id="logInterceptor" class="xxx.xxx.MethodLoggerInterceptor ">
</bean>
<bean id="testImpl" class="xxx.xxx.DumpHandlerImpl">
</bean>
</beans>
OK,完工。
分享到:
相关推荐
aop注释日志结合Java注释日志实现AOP。 要执行此项目,请在AOPLoggerTest.java中运行main方法请确保为项目启用了AspectJ Tooling,以得到正确的结果。 要在Spring Tool Suite中执行此操作, 将适当的项目导入STS, ...
Spring注释记录注释驱动的日志记录库
:warning: 该项目现在是EE4J计划的一部分。 该仓库已被归档,因为所有活动现在都在。 有关整体EE4J过渡状态,请参见。 日志注释处理器 一个Java注释处理器,用于处理与日志记录相关的注释。
编译时注解开发
commons-logging-1.2.jar spring-beans-4.0.4.RELEASE.jar spring-context-4.0.4.RELEASE.jar spring-core-4.0.4.RELEASE.jar spring-expression-4.0.4.RELEASE.jar spring-aop-4.0.4.RELEASE.jar ...
Daffodil is an Annotation-triggered method call logging library. Usage Add daffodil closure in build.gradle daffodil { enabled true } Add @ Daffodil Annotation on methods, method call's detail ...
SpringMVC(classmate-0.8.0.jar ...hibernate-validator-annotation-processor-5.0.0.CR2.jar jboss-logging-3.1.1.GA.jar validation-api-1.1.0.CR1.jar (不会出现包不兼容情况本人亲自使用) )
Annotation-triggered method call logging for your debug builds. As a programmer, you often add log statements to print method calls, their arguments, their return values, and the time it took to ...
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.support.rowset.ResultSetWrappingSqlRowSet; import org....
commons-logging-1.1.3.jar spring-aop-4.0.6.RELEASE.jar spring-beans-4.0.6.RELEASE.jar spring-context-4.0.6.RELEASE.jar spring-core-4.0.6.RELEASE.jar spring-expression-4.0.6.RELEASE.jar 在使用spring...
java.sql 提供使用 JavaTM 编程语言访问并处理存储在数据源(通常是一个关系数据库)中的数据的 API。 java.text 提供以与自然语言无关的方式来处理文本、日期、数字和消息的类和接口。 java.text.spi java.text ...
spring-aop-4.0.0.RELEASE.jar 使用Spring 的AOP 特性时所需的类和源码级元数据支持 spring-aspects-4.0.0.RELEASE.jar 提供对AspectJ的支持,以便可以方便的将面向方面的功能集成进IDE中 spring-beans-4.0.0....
nested exception is java.lang.IllegalStateException: Context namespace element 'component-scan' and its parser class [org.springframework.context.annotation.ComponentScanBeanDefinitionParser] are ...
commons-logging-1.1.jar geronimo-activation_1.1_spec-1.0-M1.jar (or Sun's Activation jar) geronimo-annotation_1.0_spec-1.1.jar (JSR 250) geronimo-javamail_1.4_spec-1.0-M1.jar (or Sun's JavaMail jar) ...
hibernate-validator-annotation-processor-5.0.0.CR2.jar jboss-logging-3.1.1.GA.jar validation-api-1.1.0.CR1.jar 【ajax】 jackson-annotations-2.1.5.jar jackson-core-2.1.5.jar jackson-databind-...
java.sql 提供使用 JavaTM 编程语言访问并处理存储在数据源(通常是一个关系数据库)中的数据的 API。 java.text 提供以与自然语言无关的方式来处理文本、日期、数字和消息的类和接口。 java.text.spi java.text ...
java.sql 提供使用 JavaTM 编程语言访问并处理存储在数据源(通常是一个关系数据库)中的数据的 API。 java.text 提供以与自然语言无关的方式来处理文本、日期、数字和消息的类和接口。 java.text.spi java.text ...
hibernate-validate涉及的相关jar包,有classmate.jar+hibernate-validator-5.jar+hibernate-validator-annotation-processor-5.jar+jboss-logging-3.1.1.jar+validation-api-1.1.0.jar
classmate-0.8.0.jar、hibernate-validator-5.0.0.CR2.jar、hibernate-validator-annotation-processor-5.0.0.CR2.jar、jboss-logging-3.1.1.GA.jar、validation-api-1.1.0.CR1.jar
│ │ │ frame-sourcefiles-org.apache.ibatis.builder.annotation.html │ │ │ frame-sourcefiles-org.apache.ibatis.builder.html │ │ │ frame-sourcefiles-org.apache.ibatis.builder.xml.html │ │ │ ...