天天看点

Spring Boot AOP实现记录操作日志

Spring Boot 实现记录操作日志

1、需求分析

​ 最近产品经理提出“要查看用户操作日志"的需求。于是就想到了用AOP来实现,为了提高效率,记录日志使用异步,日志的记录可选择保存在mysql或者MongoDB中。

2、代码实现

1、添加AOP依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>
           

2、日志注解

package com.zjson.together.mall.admins.annotation;

import java.lang.annotation.*;

/**
 * 系统日志注解
 * @author zj
 * @date 2020 2020/10/10 15:18
 * @describe
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AddSysLog {

    /**
     * 操作名
     * @return
     */
    String value() default "操作日志";

    /**
     * 添加到数据库中
     * @return
     */
    boolean intoDB() default true;

    /**
     *添加到缓存
     * @return
     */
    boolean intoCache() default false;
}

           

3、日志的切面处理

package com.zjson.together.mall.admins.annotation;

import com.google.gson.Gson;
import com.zjson.together.common.utils.HttpContextUtils;
import com.zjson.together.common.utils.IpUtils;
import com.zjson.together.mall.admins.entity.SysLog;
import com.zjson.together.mall.admins.service.SysLogService;
import lombok.extern.slf4j.Slf4j;
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.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.lang.reflect.Method;
import java.util.Date;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;

/**
 * @describe 系统日志  切面处理类
 * @author zj
 * @date 2020 2020/10/13 15:23
 */
@Slf4j
@Aspect
@Component
public class SysLogAspect {
    @Resource
    private SysLogService sysLogService;

    @Pointcut("@annotation(com.zjson.together.mall.admins.annotation.AddSysLog)")
    public void sysLogPointCut() {
    }

    @Around("sysLogPointCut()")
    public  Object around(ProceedingJoinPoint point) throws Throwable {
        long beginTime = System.currentTimeMillis();
        //执行方法
        Object result = point.proceed();
        //执行时长(毫秒)
        long time = System.currentTimeMillis() - beginTime;
        //设置IP地址
        String ip = IpUtils.getIpAddr(HttpContextUtils.getHttpServletRequest());
        //保存日志
        CompletableFuture.runAsync(() -> saveSysLog(point, time, ip)) ;
        return result;
    }

    /**
     * 保存日志
     * @param joinPoint
     * @param time
     */
    private void saveSysLog(ProceedingJoinPoint joinPoint, long time,  String ip) {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();

        SysLog sysLog = new SysLog();
        sysLog.setIp(ip);
        AddSysLog addSysLog = method.getAnnotation(AddSysLog.class);
        if (addSysLog != null) {
            //注解上的描述
            sysLog.setOperation(addSysLog.value());
        }
        //请求的方法名
        String className = joinPoint.getTarget().getClass().getName();
        String methodName = signature.getName();
        sysLog.setMethod(className + "." + methodName + "()");
        //请求的参数
        Object[] args = joinPoint.getArgs();
        try {
            String params = new Gson().toJson(args);
            sysLog.setParams(params);

            //用户名
            String userName = getUser();
            sysLog.setUserName(userName);
            sysLog.setTime(time);
            sysLog.setCreateTime(new Date());
            sysLog.setId(UUID.randomUUID().toString());
            //保存系统日志
            if (addSysLog.intoDB()) {
                sysLogService.save(sysLog);
            }
            if (addSysLog.intoCache()) {

            }
        } catch (Exception ignored) {
            log.error(ignored.getMessage());
        }
    }
}

           

4、用到的工具类

HttpContextUtils

package com.zjson.together.common.utils;

import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

/**
 * HttpContextUtils
 *
 * @author zj
 */
public class HttpContextUtils {

    public static HttpServletRequest getHttpServletRequest() {
        return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
    }

    public static String getDomain() {
        HttpServletRequest request = getHttpServletRequest();
        StringBuffer url = request.getRequestURL();
        return url.delete(url.length() - request.getRequestURI().length(), url.length()).toString();
    }

    public static String getOrigin() {
        HttpServletRequest request = getHttpServletRequest();
        return request.getHeader("Origin");
    }
}

           

IpUtils

package com.zjson.together.common.utils;

import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.http.HttpServletRequest;

/**
 * IP地址
 *
 * @author zj
 */
@Slf4j
public class IpUtils {

    /**
     * 获取IP地址
     * <p>
     * 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址
     * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址
     */
    public static String getIpAddr(HttpServletRequest request) {
        String srtUnknown = "unknown";
        String ip = null;
        try {
            ip = request.getHeader("x-forwarded-for");
            if (StringUtils.isEmpty(ip) || srtUnknown.equalsIgnoreCase(ip)) {
                ip = request.getHeader("Proxy-Client-IP");
            }
            if (StringUtils.isEmpty(ip) || ip.length() == 0 || srtUnknown.equalsIgnoreCase(ip)) {
                ip = request.getHeader("WL-Proxy-Client-IP");
            }
            if (StringUtils.isEmpty(ip) || srtUnknown.equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_CLIENT_IP");
            }
            if (StringUtils.isEmpty(ip) || srtUnknown.equalsIgnoreCase(ip)) {
                ip = request.getHeader("HTTP_X_FORWARDED_FOR");
            }
            if (StringUtils.isEmpty(ip) || srtUnknown.equalsIgnoreCase(ip)) {
                ip = request.getRemoteAddr();
            }
        } catch (Exception e) {
            log.error("IPUtils ERROR ", e);
        }
        return ip;
    }

}

           

5、使用

直接在方法头上加上注解
@ApiOperation("分页查询(排序)")
    @GetMapping("/list")
    @AddSysLog("分页查询")
    public Resp<PageVo> list(QueryCondition queryCondition) {
           

继续阅读