• [转载] Logging Standard Exceptions with Spring AOP


    Logging Standard Exceptions with Spring AOP

    Posted by Tejus Parikh on May 18, 2011

    One of the challenges of working on a large system with multiple developers is the management and handling of application errors and exceptions. Often, developers get frustrated with managing typed exceptions and all interfaces either throw or catch Exception. Another common approach is to change all exceptions to RuntimeException to avoid cluttering interfaces. An alternative approached is described in this paper by Andy Longshaw. His approach breaks exceptions into two distinct hierarchies, domain and system. Domain errors caused by logical faults in the application, such as a zero being passed as a divisor to a function. System errors are errors caused through the interaction with other components, like a network timeout when attempting to communicate with the database. Both exception types are checked exceptions, which means they must be caught and handled by each layer of the application. He presents guidelines on when and at which layer each type of exception should be logged. If you are using Java the obvious drawback of using checked exceptions everywhere is that each layer will be filled with code that looks like the following:

        try {
    
            service.doApiCall();
    
        } catch(DomainException e) {
    
            log.error("Domain exception occurred", e);
    
        } catch(SystemException e) {
    
            log.error("System exception occurred", e);
    
        }
    
    

    The duplication of handling of api exceptions can easily be handled with a custom annotation and Spring AOP. The first step is to create the Boundary logging annotation. This annotation describes where exceptions should be logged.

        @Retention(value = RetentionPolicy.RUNTIME)
    
        @Target(value = {ElementType.METHOD})
    
        public @interface BoundaryLogger {
    
        }
    
    

    Next, we need to create the interceptor. Along with just logging exceptions, I also choose to log the api calls. There are a few steps to creating an Aspect that can be used by Spring-AOP. First added the @Aspect annotation to the class declaration.

        @Service    // make this available as a spring bean
    
        @Aspect     // tell spring this class is an aspect
    
        public class BoundaryLoggingInterceptor {
    
    

    Next, create a method that tells the Aspect to do something before the underlying method, annotated with @BoundaryLogger, is called. In this example, it’s to log the method invocation.

        @Before("@annotation(net.vijedi.springlogging.interceptor.BoundaryLogger)")
    
        public void logInvocation(JoinPoint jp) {
    
    

    Similarly, triggering the aspect at method end can be handled with:

        @AfterReturning(
    
                pointcut = "@annotation(net.vijedi.springlogging.interceptor.BoundaryLogger)",
    
                returning = "retVal")
    
        public void logComplete(JoinPoint jp, Object retVal) {
    
    

    The one we are especially interested in for this purposes of this blog post is exception logging. This is the entire method for logging exceptions:

        @AfterThrowing(
    
                pointcut = "@annotation(net.vijedi.springlogging.interceptor.BoundaryLogger)",
    
                throwing = "ex"
    
        )
    
        public void processException(JoinPoint jp, Throwable ex) throws SystemException, DomainException {
    
            if(ex instanceof SystemException) {
    
                // System exceptions were logged at source
    
                // do not log the exception, just the return
    
                logReturn(jp, getLog(jp));
    
                throw (SystemException) ex;
    
            } else if(ex instanceof DomainException) {
    
                logException(jp, ex);
    
                throw (DomainException) ex;
    
            } else {
    
                logException(jp, ex);
    
                throw new DomainException(ex);
    
            }
    
    
    
        }
    
    

    The logException method is very simple:

        private void logException(JoinPoint jp, Throwable ex) {
    
            Log log = getLog(jp);
    
            log.error(ex.getMessage(), ex);
    
            logReturn(jp, log);
    
        }    
    
    

    How the logger is obtained is very important for accurate logging. Otherwise, it will appear that all logging messages are coming from the interceptor and not from the underlying method. Along with the confusion this could cause, it will also make it harder to use automatic log analyzers. However, it is very easy to get a logger in the context of the underlying class.

        protected Log getLog(JoinPoint jp) {
    
            return LogFactory.getLog(jp.getTarget().getClass());
    
        }
    
    

    Once the Aspect has been created, the final step is to tell spring what to do when it encounters the annotation @BoundaryLogger. There are four steps, tell spring to use annotations to configure the environment, to scan the packages the annotations are in, and to create the aop environment.

        

    This is all that’s need to use annotations for logging at the tier boundaries. The complete code for the example is available at my Spring Boundary Logging repo on Github.

    - See more at: http://www.tejusparikh.com/2011/logging-standard-exceptions-with-spring-.html#sthash.MNPx8RnM.dpuf

  • 相关阅读:
    Java多线程系列 JUC锁03 公平锁(一)
    Java多线程系列 JUC锁02 互斥锁ReentrantLock
    JDBC课程3--通过ResultSet执行查询操作
    JDBC课程2--实现Statement(用于执行SQL语句)--使用自定义的JDBCTools的工具类静态方法,包括insert/update/delete三合一
    JDBC_通过DriverManager获得数据库连接
    JDBC课程1-实现Driver接口连接mysql数据库、通用的数据库连接方法(使用文件jdbc.properties)
    [终章]进阶20-流程控制结构--if/case/while结构
    MySQL进阶19--函数的创建(举例)/设置mysql的创建函数的权限/查看(show)/删除(drop) / 举4个栗子
    MySQL进阶18- 存储过程- 创建语句-参数模式(in/out/inout-对应三个例子) -调用语法-delimiter 结束标记'$'- 删除/查看/修改-三个练习
    SQL进阶17-变量的声明/使用(输出)--全局变量/会话变量--用户变量/局部变量
  • 原文地址:https://www.cnblogs.com/yuxiaoqi/p/4617882.html
Copyright © 2020-2023  润新知