美文网首页Java
Spring切面编程(AOP)-拦截API请求打印日志

Spring切面编程(AOP)-拦截API请求打印日志

作者: AC编程 | 来源:发表于2022-03-16 09:54 被阅读0次

一、实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
@ApiModel(value = "api请求日志记录")
public class LogApi {

    @ApiModelProperty(value = "线程ID")
    private String threadId;

    @ApiModelProperty(value = "线程名称")
    private String threadName;

    @ApiModelProperty(value = "ip")
    private String ip;

    @ApiModelProperty(value = "url")
    private String url;

    @ApiModelProperty(value = "http方法 GET POST PUT DELETE PATCH")
    private String httpMethod;

    @ApiModelProperty(value = "类方法")
    private String classMethod;

    @ApiModelProperty(value = "请求参数")
    private Object requestParams;

    @ApiModelProperty(value = "返回参数")
    private Object result;

    @ApiModelProperty(value = "接口耗时")
    private Long timeCost;

    @ApiModelProperty(value = "是否为移动平台")
    private boolean mobile;

    @ApiModelProperty(value = "浏览器类型")
    private String browser;

    @ApiModelProperty(value = "平台类型")
    private String platform;

    @ApiModelProperty(value = "系统类型")
    private String os;

    @ApiModelProperty(value = "引擎类型")
    private String engine;

    @ApiModelProperty(value = "浏览器版本")
    private String version;

    @ApiModelProperty(value = "引擎版本")
    private String engineVersion;
}

二、AOP拦截

@Aspect
@Component
@Slf4j
public class AOPLogApi {

    private static final String UNKNOWN = "unknown";

    /**
     * 日志切入点 - 正常执行<br>
     * 表达式1:拦截所有controller
     * 表达式2:排除拦截ApiLogController中的方法
     */
    @Pointcut(
        "execution(public * com.alanchen.*.controller.*Controller.*(..))" +
        "&& !execution(public * com.alanchen.*.controller.ApiLogController.*(..))" +
        "&& !execution(public * com.alanchen.*.controller.SensitiveWordController.selectList(..))" +
        "&& !execution(public * com.alanchen.*.controller.SensitiveWordController.test(..))"
    )
    public void logPointCut() {

    }

    /**
     * 日志切入点 - 异常
     */
    @Pointcut(
            "execution(public * com.alanchen.*.controller.*Controller.*(..))"
    )
    public void logExceptionPointCut() {

    }

    /**
     * 正常执行
     * @param point 切入点
     * @throws Throwable 异常信息
     */
    @Around("logPointCut()")
    public Object logAround(ProceedingJoinPoint point) throws Throwable {
        Object result = point.proceed();
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        if (attributes != null) {
            HttpServletRequest request = attributes.getRequest();

            // 打印请求相关参数
            long startTime = System.currentTimeMillis();
            String ua = request.getHeader("User-Agent");
            UserAgent userAgent = UserAgentUtil.parse(ua);
            ApiLogVO logApi = new ApiLogVO();
            //线程ID
            logApi.setThreadId(Long.toString(Thread.currentThread().getId()));
            //线程名
            logApi.setThreadName(Thread.currentThread().getName());
            //IP
            logApi.setIp(getIp(request));
            //访问URI
            logApi.setUrl(request.getRequestURL().toString());
            //http方法
            logApi.setHttpMethod(request.getMethod());
            //类方法
            logApi.setClassMethod(point.getSignature().getDeclaringTypeName() + "." + point.getSignature().getName());
            //请求参数
            logApi.setRequestParams(getNameAndValue(point));
            //返回参数
           // logApi.setResult(JSONUtil.toJsonStr(result));
            //接口耗时
            logApi.setTimeCost(System.currentTimeMillis() - startTime);
            //是否为移动平台
            logApi.setMobile(userAgent.isMobile());
            //浏览器类型
            logApi.setBrowser(userAgent.getBrowser().getName());
            //平台类型
            logApi.setPlatform(userAgent.getPlatform().getName());
            //系统类型
            logApi.setOs(userAgent.getOs().getName());
            //引擎类型
            logApi.setEngine(userAgent.getEngine().getName());
            //浏览器版本
            logApi.setVersion(userAgent.getVersion());
            //引擎版本
            logApi.setEngineVersion(userAgent.getEngineVersion());
            //操作时间
            logApi.setCreateTime(LocalDateTime.now());
            log.info("API请求信息: {}", JSONUtil.toJsonStr(logApi));
        }

        return result;
    }

    /**
     * 异常
     * @param point 切入点
     * @param e     异常
     */
    @AfterThrowing(pointcut = "logExceptionPointCut()", throwing = "e")
    public void logExceptionAround(JoinPoint point, Throwable e) {
        e.printStackTrace();
    }

    /**
     * 获取ip
     * @param request
     * @return
     */
    private String getIp(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        String comma = ",";
        String localhost = "127.0.0.1";
        if (ip.contains(comma)) {
            ip = ip.split(",")[0];
        }
        if (localhost.equals(ip)) {
            // 获取本机真正的ip地址
            try {
                ip = InetAddress.getLocalHost().getHostAddress();
            } catch (UnknownHostException e) {
                log.error(e.getMessage(), e);
            }
        }
        return ip;
    }

    /**
     *  获取方法参数名和参数值
     * @param joinPoint
     * @return
     */
    private Map<String, Object> getNameAndValue(ProceedingJoinPoint joinPoint) {

        final Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        final String[] names = methodSignature.getParameterNames();
        final Object[] args = joinPoint.getArgs();

        if (ArrayUtil.isEmpty(names) || ArrayUtil.isEmpty(args)) {
            return Collections.emptyMap();
        }
        if (names.length != args.length) {
            log.warn("{}方法参数名和参数值数量不一致", methodSignature.getName());
            return Collections.emptyMap();
        }
        Map<String, Object> map = Maps.newHashMap();
        for (int i = 0; i < names.length; i++) {
            map.put(names[i], args[i]);
        }
        return map;
    }
}

相关文章

网友评论

    本文标题:Spring切面编程(AOP)-拦截API请求打印日志

    本文链接:https://www.haomeiwen.com/subject/uijydrtx.html