zoukankan      html  css  js  c++  java
  • 基于aop的日志记录方式实现

    说明

    最近有个项目需要增加日志记录功能,因为这个项目原来是基于spring开发的,在查阅了相关资料以后,我采用了spring aop的方式实现该需求,然后就有了本篇文章。

    思路

    我这边需求是这样的:要统计各个接口的调用次数,然后我想加上客户端ip、请求参数、返回结果等信息,而我的接口都是通过controller实现的,所以从原理上讲就是要通过aop拦截所有controller,实现日志记录。项目本身用到了swagger,每个接口都有ApiOperation注解,所以我的实现方式就变成了拦截ApiOperation注解,为什么要拦截ApiOperation,因为ApiOperation注解有对每个接口的描述信息,当然也是因为每个接口方法上都有ApiOperation注解,当然如果你愿意你也可以拦截RequestMapping,下面是我的aop实现:

    package com.mytest.aop;
    
    import io.swagger.annotations.ApiOperation;
    import org.apache.shiro.SecurityUtils;
    import org.apache.shiro.subject.Subject;
    import org.aspectj.lang.ProceedingJoinPoint;
    import org.aspectj.lang.annotation.Around;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Pointcut;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import javax.servlet.http.HttpServletRequest;
    import java.lang.reflect.Method;
    import java.text.SimpleDateFormat;
    import java.util.Date;
    
    /**
     * @program: publicCoreServer
     * @description: aop 打印 接收参数日志
     * @author: liu yan
     * @create: 2019-11-21 09:15
     */
    
    
    @Aspect
    @Component//定义一个切面
    public class LogRecordAspect {
        private static final Logger logger = LoggerFactory.getLogger(LogRecordAspect.class);
    
        private static final String UTF_8 = "utf-8";
    
        @Autowired
        private UpmsLogService upmsLogService;
    
    
        @Pointcut("@annotation(io.swagger.annotations.ApiOperation)")
        public void logPointCut() {
    
        }
    
    
        @Around("logPointCut()")
        public Object around(ProceedingJoinPoint point) throws Throwable {
            long beginTime = System.currentTimeMillis();
            //执行方法
            Object result = point.proceed();
            //执行时长(毫秒)
            long time = System.currentTimeMillis() - beginTime;
    
            //保存日志
            saveSysLog(point, beginTime, time, result);
    
            return result;
        }
    
        /**
         * 日志记录
         *
         * @param joinPoint
         * @param beginTime 开始时间
         * @param spendTime  用时
         * @param result  返回结果
         */
        private void saveSysLog(ProceedingJoinPoint joinPoint,long beginTime, long spendTime, Object result) {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
    
            UpmsLog upmsLog = new UpmsLog();
            upmsLog.setLogId(UUIDUtil.getUUID());
            upmsLog.setAppId("wx");
    
            ApiOperation annotation = method.getAnnotation(ApiOperation.class);
            if(annotation != null){
                //注解上的描述
                upmsLog.setDescription(annotation.value());
            }
    
            //请求的参数
            Object[] args = joinPoint.getArgs();
            String params = JSON.toJSONString(args[0]);
            upmsLog.setParameter(params);
    
    
            // 返回结果
            String resultJson = JSON.toJSONString(result);
            upmsLog.setResult(resultJson);
    
            //获取request
            HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
            //设置IP地址
            upmsLog.setIp(IPUtils.getIpAddr(request));
            // 请求路径
            String requestUrl = request.getRequestURL().toString();
            upmsLog.setUrl(requestUrl);
            String requestURI = request.getRequestURI();
            upmsLog.setUri(requestURI);
    
            // 请求头
            String agentString = request.getHeader("User-Agent");
            upmsLog.setUserAgent(agentString);
    
            // 请求方式
            String authType = request.getMethod();
            upmsLog.setMethod(authType);
    
            //用户名
            Subject subject = SecurityUtils.getSubject();
            if (subject.isAuthenticated()) {
                Object userId = subject.getPrincipal();
                upmsLog.setUsername(userId.toString());
                upmsLog.setUserId(userId.toString());
            }
    
            String path = request.getContextPath();
            String baseURL =  request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
            upmsLog.setBasePath(baseURL);
    
            // 开始时间
            upmsLog.setStartTime(beginTime);
            // 用时
            upmsLog.setSpendTime(spendTime);
            upmsLog.setCreatetime(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()));
            //保存系统日志
            logger.info("日志信息:"+ upmsLog);
            upmsLogService.saveLog(upmsLog);
        }
    
    
    }
    

    我的spring xml的配置如下:

     <context:component-scan
                base-package="com.mytest.aop">
            <context:include-filter type="annotation"
                                    expression="org.aspectj.lang.annotation.Aspect" />
        </context:component-scan>
    
        <!-- 设置AOP为自动代理 -->
        <aop:aspectj-autoproxy proxy-target-class="true"/>
    

    我的controller(部分)

    @Api(value = "info", description = "相关控制器")
    @Controller
    @RequestMapping(value = "/test/getInfo")
    public class InfoController {
    
        @Autowired
        private InfoService infoService;
    
        @ApiOperation(value = "aop日志测试")
        @RequestMapping("/getInfo")
        @ResponseBody
        public AjaxJson getInfo(String datas) {
            JSONObject data = JSONObject.parseObject(datas);
    
            AjaxJson result = infoService.getInfo(data.toString());
            return result;
        }
    }
    

    其他的servic、entity等代码这里就不放了,有需求的小伙伴可以留言。

    解释

    这里简单说明下,aop的实现中最核心最重要的就是切点的定义,也就是如下代码:

     @Pointcut("@annotation(io.swagger.annotations.ApiOperation)")
        public void logPointCut() {
    
        }
    
    

    上面定义了类型为注解的切点,切点方法及方式如下:

     @Around("logPointCut()")
        public Object around(ProceedingJoinPoint point) throws Throwable {
            long beginTime = System.currentTimeMillis();
            //执行方法
            Object result = point.proceed();
            //执行时长(毫秒)
            long time = System.currentTimeMillis() - beginTime;
    
            //保存日志
            saveSysLog(point, beginTime, time, result);
    
            return result;
        }
    

    我们aop作用的方式是Around,也就是环绕,为啥要用这个,因为我们要拿到方法的入参和返回值,所以这个就是最优解。对于aop,好多知识点早都忘记了,而且平时基本没有用到过,后面一定要抽时间做一次研究,深入探讨一下。

    最近确实没有太多时间来学习,而且晚上回来太晚了,家里也没有电脑,只能利用周末时间来学习、整理和总结。再过段时间,尽量保证每天能抽时间写点东西,谢谢大家支持,晚安!

  • 相关阅读:
    如何理解cat的输入输出重定向
    Vagrant网络设置
    Vagrant入门
    设计模式的原则
    单例模式
    LRU 实现缓存
    java注解
    java8---lambda表达式
    JUC--Callable 以及Lock同步锁
    JUC--闭锁 CountDownLatch
  • 原文地址:https://www.cnblogs.com/caoleiCoding/p/11920879.html
Copyright © 2011-2022 走看看