`
taojingrui
  • 浏览: 66763 次
  • 来自: ...
社区版块
存档分类
最新评论

用annotation处理logging

阅读更多

前段时间,项目中讨论了改进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-annotation-logging

    aop注释日志结合Java注释日志实现AOP。 要执行此项目,请在AOPLoggerTest.java中运行main方法请确保为项目启用了AspectJ Tooling,以得到正确的结果。 要在Spring Tool Suite中执行此操作, 将适当的项目导入STS, ...

    spring-annotation-logging:注释驱动的日志记录库

    Spring注释记录注释驱动的日志记录库

    logging-annotation-processor:一个Java注释处理器,用于处理与日志相关的注释

    :warning: 该项目现在是EE4J计划的一部分。 该仓库已被归档,因为所有活动现在都在。 有关整体EE4J过渡状态,请参见。 日志注释处理器 一个Java注释处理器,用于处理与日志记录相关的注释。

    编译时注解开发

    编译时注解开发

    spring-aop-annotation-log-all

    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 ...

    Android代码-基于ASM,通过注解,实现对方法调用时的参数、返回值、耗时等信息的纪录。

    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处理Jsno.rar

    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 (不会出现包不兼容情况本人亲自使用) )

    Android代码-hugo

    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 ...

    spring jdbctemplate 封裝

    import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.support.rowset.ResultSetWrappingSqlRowSet; import org....

    springAOP demo 带错误解决文档

    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_API1.6文档(中文)

    java.sql 提供使用 JavaTM 编程语言访问并处理存储在数据源(通常是一个关系数据库)中的数据的 API。 java.text 提供以与自然语言无关的方式来处理文本、日期、数字和消息的类和接口。 java.text.spi java.text ...

    基于SpringMVC+Hibernate4的考勤管理系统+.zip

    spring-aop-4.0.0.RELEASE.jar 使用Spring 的AOP 特性时所需的类和源码级元数据支持 spring-aspects-4.0.0.RELEASE.jar 提供对AspectJ的支持,以便可以方便的将面向方面的功能集成进IDE中 spring-beans-4.0.0....

    Spring 3.0所需jar文件和对应的配置文件

    nested exception is java.lang.IllegalStateException: Context namespace element 'component-scan' and its parser class [org.springframework.context.annotation.ComponentScanBeanDefinitionParser] are ...

    cxf-2.1.4.jar

    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) ...

    关于常用的整合ssm框架

    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 1.6 API 中文 New

    java.sql 提供使用 JavaTM 编程语言访问并处理存储在数据源(通常是一个关系数据库)中的数据的 API。 java.text 提供以与自然语言无关的方式来处理文本、日期、数字和消息的类和接口。 java.text.spi java.text ...

    [Java参考文档].JDK_API 1.6

    java.sql 提供使用 JavaTM 编程语言访问并处理存储在数据源(通常是一个关系数据库)中的数据的 API。 java.text 提供以与自然语言无关的方式来处理文本、日期、数字和消息的类和接口。 java.text.spi java.text ...

    hibernate-validate.zip

    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

    自定义注解进行参数校验的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

    前端-后端java的Util类的工具类

    │ │ │ frame-sourcefiles-org.apache.ibatis.builder.annotation.html │ │ │ frame-sourcefiles-org.apache.ibatis.builder.html │ │ │ frame-sourcefiles-org.apache.ibatis.builder.xml.html │ │ │ ...

Global site tag (gtag.js) - Google Analytics