概述


使用spring aop 来实现日志的统一收集功能


详细




使用spring aop 来实现日志的统一收集功能。

spring aop 配置

首先,我们定义2种注解,一种是给service用的,一种是给Controller用的。

给service使用的aop扫描


<aop:aspectj-autoproxy />

<context:annotation-config />

<context:component-scan base-package="com.demodashi">
	<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>

<tx:annotation-driven />


给Controller使用的aop扫描


<aop:aspectj-autoproxy />
<aop:aspectj-autoproxy proxy-target-class="true" />

<!-- 扫描web包,应用Spring的注解 -->
<context:component-scan	base-package="com.demodashi">
	<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
	<context:exclude-filter type="annotation" expression="javax.inject.Named" />
	<context:exclude-filter type="annotation" expression="javax.inject.Inject" />
</context:component-scan>



java实现

实现思路,先定义两个注解类,一个给service类用的,一个给从controller类用的,然后使用切面类,对这两个注解进行绑定监控。结果就是,当使用注解绑定某个service类或者controller类的某个方法时,这个切面类就能监控到,并且能获取到这个service方法的相关输入,输出参数等。这样,就能实现了aop日志了。

给controller使用的注解类
package com.demodashi.aop.annotation;
import java.lang.annotation.*;    
    
/**  
 *自定义注解 拦截Controller  
 */    
    
@Target({ElementType.PARAMETER, ElementType.METHOD})    
@Retention(RetentionPolicy.RUNTIME)    
@Documented    
public  @interface ControllerLogAnnotation {    
    String description()  default "";    
}
给service使用的注解类
package com.demodashi.aop.annotation;
import java.lang.annotation.*;    
    
/**  
 *自定义注解 拦截service  
 */    
    
@Target({ElementType.PARAMETER, ElementType.METHOD})    
@Retention(RetentionPolicy.RUNTIME)    
@Documented    
public  @interface ServiceLogAnnotation {    
    
    String description()  default "";    
}
日志切面类
package com.demodashi.aop;

import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import com.alibaba.fastjson.JSONObject;
import com.demodashi.aop.annotation.ControllerLogAnnotation;
import com.demodashi.aop.annotation.ServiceLogAnnotation;
import com.demodashi.base.UserVO;

/**
 * 切点类   
 * @author xgchen
 *
 */
@Aspect    
@Component    
public  class SystemLogAspect {    
    
	public SystemLogAspect(){
	}
	
    //本地异常日志记录对象    
    private  static  final Logger logger = LoggerFactory.getLogger(SystemLogAspect.class);    
    
    //Service层切点    
    @Pointcut("@annotation(com.demodashi.aop.annotation.ServiceLogAnnotation)")    
    public  void serviceAspect() {
    }
    
    //Controller层切点    
    @Pointcut("@annotation(com.demodashi.aop.annotation.ControllerLogAnnotation)")    
    public  void controllerAspect() {    
    }
    
    /**  
     * 前置通知 用于拦截Controller层记录用户的操作  
     *  
     * @param joinPoint 切点  
     */
    @Before("controllerAspect()")
	public  void doBefore4control(JoinPoint joinPoint) {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();    
        HttpSession session = request.getSession();    
        //读取session中的用户    
        UserVO user = (UserVO) session.getAttribute("USER");
        //请求的IP    
        String ip = request.getRemoteAddr();    
         try {    
            //*========控制台输出=========*//    
            System.out.println("=====control 前置通知开始=====");
            System.out.println("请求方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));    
            System.out.println("方法描述:" + getControllerMethodDescription(joinPoint));    
            System.out.println("请求人ID:" + user.getId());
            System.out.println("请求人NAME:" + user.getName());
            System.out.println("请求IP:" + ip);    
            System.out.println("=====前置通知结束=====");    
        }  catch (Exception e) {
            //记录本地异常日志    
            logger.error("==前置通知异常==");
            logger.error("异常信息:{}", e.getMessage());    
        }
    }
    
    @Before("serviceAspect()")
    public  void doBefore4service(JoinPoint joinPoint) {
    	HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();    
        HttpSession session = request.getSession();    
        //读取session中的用户    
        UserVO user = (UserVO) session.getAttribute("USER");    
        //获取请求ip    
        String ip = request.getRemoteAddr();
        //获取用户请求方法的参数并序列化为JSON格式字符串    
        String params = "";    
         if (joinPoint.getArgs() !=  null && joinPoint.getArgs().length > 0) {    
             for ( int i = 0; i < joinPoint.getArgs().length; i++) {    
                params += JSONObject.toJSON(joinPoint.getArgs()[i]).toString() + ";";
            }  
        }
        try {    
            /*========控制台输出=========*/    
        	System.out.println("=====service 前置通知开始=====");
            System.out.println("异常方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));    
            System.out.println("方法描述:" + getServiceMthodDescription(joinPoint));    
            System.out.println("请求人ID:" + user.getId());
            System.out.println("请求人NAME:" + user.getName());
            System.out.println("请求IP:" + ip);
            System.out.println("请求参数:" + params);
            
        }  catch (Exception ex) {    
            //记录本地异常日志    
            logger.error("==异常通知异常==");    
            logger.error("异常信息:{}", ex.getMessage());    
        }    
    }
    
    @AfterReturning(pointcut="serviceAspect()", returning="returnValue")
    public  void after4service(JoinPoint joinPoint, Object returnValue) {
    	HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();    
        HttpSession session = request.getSession();    
        //读取session中的用户    
        UserVO user = (UserVO) session.getAttribute("USER");    
        //获取请求ip    
        String ip = request.getRemoteAddr();
        //获取用户请求方法的参数并序列化为JSON格式字符串    
        String params = "";
         if (joinPoint.getArgs() !=  null && joinPoint.getArgs().length > 0) {    
             for ( int i = 0; i < joinPoint.getArgs().length; i++) {    
                params += JSONObject.toJSON(joinPoint.getArgs()[i]).toString() + ";";
            }  
        }
        try {    
            /*========控制台输出=========*/    
        	System.out.println("=====service 后置通知开始=====");
            System.out.println("异常方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));    
            System.out.println("方法描述:" + getServiceMthodDescription(joinPoint));    
            System.out.println("请求人ID:" + user.getId());
            System.out.println("请求人NAME:" + user.getName());
            System.out.println("请求IP:" + ip);
            System.out.println("请求参数:" + params);
            System.out.println("返回值为:" + JSONObject.toJSON(returnValue).toString());
        }  catch (Exception ex) {    
            //记录本地异常日志    
            logger.error("==异常通知异常==");    
            logger.error("异常信息:{}", ex.getMessage());    
        }
    }
    
    /**  
     * 异常通知 用于拦截service层记录异常日志  
     *  
     * @param joinPoint  
     * @param e  
     */    
    @AfterThrowing(pointcut = "serviceAspect()", throwing = "e")    
	public  void doAfterThrowing(JoinPoint joinPoint, Throwable e) {    
    	HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();    
        HttpSession session = request.getSession();    
        //读取session中的用户    
        UserVO user = (UserVO) session.getAttribute("USER"); 
        //获取请求ip    
        String ip = request.getRemoteAddr();    
        //获取用户请求方法的参数并序列化为JSON格式字符串    
        String params = "";    
         if (joinPoint.getArgs() !=  null && joinPoint.getArgs().length > 0) {    
             for ( int i = 0; i < joinPoint.getArgs().length; i++) {    
            	 params += JSONObject.toJSON(joinPoint.getArgs()[i]).toString() + ";";
            }  
        }
        try {    
            /*========控制台输出=========*/    
            System.out.println("=====异常通知开始=====");
            System.out.println("异常代码:" + e.getClass().getName());    
            System.out.println("异常信息:" + e.getMessage());    
            System.out.println("异常方法:" + (joinPoint.getTarget().getClass().getName() + "." + joinPoint.getSignature().getName() + "()"));    
            System.out.println("方法描述:" + getServiceMthodDescription(joinPoint));    
            System.out.println("请求人ID:" + user.getId());
            System.out.println("请求人NAME:" + user.getName());
            System.out.println("请求IP:" + ip);
            System.out.println("请求参数:" + params);
            
            System.out.println("=====异常通知结束=====");    
        }  catch (Exception ex) {    
            //记录本地异常日志    
            logger.error("==异常通知异常==");    
            logger.error("异常信息:{}", ex.getMessage());    
        }    
         /*==========记录本地异常日志==========*/    
        logger.error("异常方法:{}异常代码:{}异常信息:{}参数:{}", joinPoint.getTarget().getClass().getName() + joinPoint.getSignature().getName(), e.getClass().getName(), e.getMessage(), params);    
    
    }    
    
    
    /**  
     * 获取注解中对方法的描述信息 用于service层注解  
     *  
     * @param joinPoint 切点  
     * @return 方法描述  
     * @throws Exception  
     */    
     public  static String getServiceMthodDescription(JoinPoint joinPoint)
             throws Exception {    
        String targetName = joinPoint.getTarget().getClass().getName();    
        String methodName = joinPoint.getSignature().getName();    
        Object[] arguments = joinPoint.getArgs();    
        Class targetClass = Class.forName(targetName);    
        Method[] methods = targetClass.getMethods();    
        String description = "";    
         for (Method method : methods) {    
             if (method.getName().equals(methodName)) {    
                Class[] clazzs = method.getParameterTypes();    
                 if (clazzs.length == arguments.length) {    
                    description = method.getAnnotation(ServiceLogAnnotation. class).description();    
                     break;    
                }    
            }    
        }    
         return description;    
    }    
    
    /**  
     * 获取注解中对方法的描述信息 用于Controller层注解  
     *  
     * @param joinPoint 切点  
     * @return 方法描述  
     * @throws Exception  
     */    
     public  static String getControllerMethodDescription(JoinPoint joinPoint)  throws Exception {    
        String targetName = joinPoint.getTarget().getClass().getName();    
        String methodName = joinPoint.getSignature().getName();    
        Object[] arguments = joinPoint.getArgs();    
        Class targetClass = Class.forName(targetName);    
        Method[] methods = targetClass.getMethods();    
        String description = "";    
         for (Method method : methods) {    
             if (method.getName().equals(methodName)) {    
                Class[] clazzs = method.getParameterTypes();    
                 if (clazzs.length == arguments.length) {    
                    description = method.getAnnotation(ControllerLogAnnotation. class).description();    
                     break;    
                }    
            }    
        }    
         return description;    
    }    
}
aop使用

就是将注解绑定到具体的service方法上面,或者control方法,如下所示:

@ServiceLogAnnotation(description = "修改密码")
@Override
public UserVO changePassword(UserVO vo, String newPassword) {
	vo.setPassword(newPassword);
	return vo;
}
@ResponseBody
@RequestMapping("/editPassword.do")
@ControllerLogAnnotation(description = "接受修改密码的请求")
public Map changePassword(ModelMap modelMap, HttpServletRequest request,
		HttpServletResponse response) throws IOException {
	String message = null;
	String result = null;
	Object vo = request.getSession().getAttribute("USER");
	if (vo == null) {
		message = "操作失败:对象不能为空!";
	} else if (StringUtils.isBlank(request.getParameter("newPassword"))) {
		message = "新登陆密码不能为空!";
	}
	if (message == null) {
		try {
			userApplication.changePassword((UserVO)vo, request.getParameter("newPassword"));
			message = "修改成功!";
			result = ConstantBean.SUCCESS;
		} catch (Exception e) {
			message = e.getMessage();
			result = ConstantBean.SYSERR;
		}
	} else {
		result = ConstantBean.SYSERR;
	}
	
	return toMap("data", message, "result", result);
}

运行起来

首把demo导入到eclipse后,运行的界面如下:

spring aop实现日志收集_java

用户名 1001 密码 123

登陆后,修改密码,则看到eclipse控制台打印如下信息:

spring aop实现日志收集_日志收集_02

这样一个aop收集日志的功能了,这样的方式比直接把log写在具体的方法上要强多了,收集起来的log,可以直接写在本地,也可以接入elk方案。

接入elk方案可以参考本网站中的:《ELK + kafka 日志方案》



注:本文著作权归作者,由demo大师宣传,拒绝转载,转载需要作者授权