SpringBoot的AOP操作日志记录使用

290 阅读3分钟

使用AOP的注解来实现操作日志记录

此处展示了AOP常用字段的获取结果,如果需要记录到表,请扩展设计即可。

依赖(其他依赖包含hutool和fastjson等)

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

注解类

/**
 * 操作日志记录注解
 * Target:该注解作用于修饰方法
 * Retention:注解生命周期
 * Documented:表明这个注解应该被javadoc工具记录
 *
 * @author Zed
 */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperLog {

    String value() default "";
}

注解处理

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.TimeInterval;
import com.alibaba.fastjson.JSON;
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.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;

/**
 * 操作日志记录处理
 *
 * @author Zed
 */
@Slf4j
@Aspect
@Component
public class LogAspect {

    @Autowired
    private HttpServletRequest httpServletRequest;

    @Pointcut("@annotation(com.practise.zed.aop.OperLog)")
    public void pointcut() {
        // do nothing
    }

    @Around("pointcut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
    
        TimeInterval timer = DateUtil.timer();
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();

        Object proceed = point.proceed();

        OperLog logAnnotation = method.getAnnotation(OperLog.class);
        String value = logAnnotation.value();

        // 请求描述
        log.info("请求描述:{}", value);

        // 请求方式
        log.info("请求方式:{}", httpServletRequest.getMethod());

        // 请求地址
        log.info("请求地址:{}", httpServletRequest.getRequestURL());

        // 操作方法(请求的类名.请求的方法名())
        String methodName = signature.getDeclaringTypeName() + "." + signature.getName() + "()";
        log.info("操作方法:{}", methodName);

        // 请求参数
        String params = JSON.toJSONString(point.getArgs());
        log.info("请求参数:{}", params);

        // 请求IP
        String ipAddress = IpUtils.getIpAddress(httpServletRequest);
        log.info("请求IP:{}", ipAddress);

        // 请求返回
        log.info("请求返回:{}", JSON.toJSONString(proceed));

        // 接口耗时
        log.info("耗时:{}ms", timer.interval());

        return proceed;
    }


}

API测试

/**
 * 模拟一次http调用
 */
@Slf4j
@RestController
public class TestController {

    @OperLog("测试")
    @PostMapping("/test")
    public String test(@RequestBody DataRequest request) {

        log.info(request);

        return "success";
    }
}

控制台打印如下:

LogAspect           : 请求描述:测试
LogAspect           : 请求方式:POST
LogAspect           : 请求地址:http://localhost:8080/test
LogAspect           : 操作方法:com.practise.zed.demo.TestController.test()
LogAspect           : 请求参数:[{"count":0,"id":0,"name":"String"}]
LogAspect           : 请求IP:127.0.0.1
LogAspect           : 请求返回:"success"
LogAspect           : 耗时:204ms

附上IP工具类


import cn.hutool.extra.servlet.ServletUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * 获取IP地址
 *
 * @author Zed
 */
@Slf4j
public class IpUtils {
    private static final String IP_UTILS_FLAG = ",";
    private static final String LOCALHOST_IP = "0:0:0:0:0:0:0:1";
    private static final String LOCALHOST_IP1 = "127.0.0.1";

    /**
     * 获取IP地址
     *
     * <p>
     * 使用Nginx等反向代理软件, 则不能通过request.getIpAddress()获取IP地址
     * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址
     * </p>
     */
    public static String getIpAddress(HttpServletRequest request) {
        String ip = ServletUtil.getClientIP(request, "X-Original-Forwarded-For");
        if (LOCALHOST_IP1.equalsIgnoreCase(ip) || LOCALHOST_IP.equalsIgnoreCase(ip)) {
            // 根据网卡取本机配置的IP
            InetAddress iNet;
            try {
                iNet = InetAddress.getLocalHost();
                ip = iNet.getHostAddress();
            } catch (UnknownHostException e) {
                log.error("getClientIp error", e);
            }

        }
        // 使用代理,则获取第一个IP地址
        if (StringUtils.isNotBlank(ip) && ip.indexOf(IP_UTILS_FLAG) > 0) {
            ip = ip.substring(0, ip.indexOf(IP_UTILS_FLAG));
        }
        return ip;
    }


    /**
     * 判断是否为手机浏览器
     *
     * @param request
     * @return
     */
    public static boolean judgeIsMobile(HttpServletRequest request) {
        boolean isMobile = false;
        String[] mobileAgents = {"iphone", "android", "ipad", "phone", "mobile", "wap", "netfront", "java", "opera mobi",
                "opera mini", "ucweb", "windows ce", "symbian", "series", "webos", "sony", "blackberry", "dopod",
                "nokia", "samsung", "palmsource", "xda", "pieplus", "meizu", "midp", "cldc", "motorola", "foma",
                "docomo", "up.browser", "up.link", "blazer", "helio", "hosin", "huawei", "novarra", "coolpad", "webos",
                "techfaith", "palmsource", "alcatel", "amoi", "ktouch", "nexian", "ericsson", "philips", "sagem",
                "wellcom", "bunjalloo", "maui", "smartphone", "iemobile", "spice", "bird", "zte-", "longcos",
                "pantech", "gionee", "portalmmm", "jig browser", "hiptop", "benq", "haier", "^lct", "320x320",
                "240x320", "176x220", "w3c ", "acs-", "alav", "alca", "amoi", "audi", "avan", "benq", "bird", "blac",
                "blaz", "brew", "cell", "cldc", "cmd-", "dang", "doco", "eric", "hipt", "inno", "ipaq", "java", "jigs",
                "kddi", "keji", "leno", "lg-c", "lg-d", "lg-g", "lge-", "maui", "maxo", "midp", "mits", "mmef", "mobi",
                "mot-", "moto", "mwbp", "nec-", "newt", "noki", "oper", "palm", "pana", "pant", "phil", "play", "port",
                "prox", "qwap", "sage", "sams", "sany", "sch-", "sec-", "send", "seri", "sgh-", "shar", "sie-", "siem",
                "smal", "smar", "sony", "sph-", "symb", "t-mo", "teli", "tim-", "tosh", "tsm-", "upg1", "upsi", "vk-v",
                "voda", "wap-", "wapa", "wapi", "wapp", "wapr", "webc", "winw", "winw", "xda", "xda-",
                "Googlebot-Mobile"};
        if (request.getHeader("User-Agent") != null) {
            String agent = request.getHeader("User-Agent");
            for (String mobileAgent : mobileAgents) {
                if (agent.toLowerCase().contains(mobileAgent) && agent.toLowerCase().indexOf("windows nt") <= 0 && agent.toLowerCase().indexOf("macintosh") <= 0) {
                    isMobile = true;
                    break;
                }
            }
        }
        return isMobile;
    }
}