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,好多知识点早都忘记了,而且平时基本没有用到过,后面一定要抽时间做一次研究,深入探讨一下。

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

  • 相关阅读:
    AtCoder Beginner Contest 167
    AtCoder Beginner Contest 166
    AtCoder Beginner Contest 165
    AtCoder Beginner Contest 164
    AtCoder Beginner Contest 163
    AtCoder Beginner Contest 162
    AtCoder Beginner Contest 161
    AtCoder Beginner Contest 160
    AtCoder Beginner Contest 159
    自定义Mybatis自动生成代码规则
  • 原文地址:https://www.cnblogs.com/caoleiCoding/p/11920879.html
Copyright © 2011-2022 走看看