【工具类】AOP记录接口访问日志

        虽然容器都有记录接口的访问日志,但是毕竟不是自己程序记录的,由于各种原因,也会出现偏差,再者,容器记录的访问日志,不一定满足自己的需求,所以,这时就需要用到AOP,针对接口层做日志记录

具体代码如下:

package cn.xdf.xadd.aop;


import cn.xdf.xadd.context.UserInfoContext;
import cn.xdf.xadd.lang.util.Result;
import cn.xdf.xadd.util.RealIpUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.RandomStringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.Map;


/**
 * AOP日志记录
 *
 * @author zhurunhua
 * @date 2020-08-11 16:47
 */
@Aspect
@Component
@Slf4j
public class LogRecordAspect {
    /**
     * 定义切面表达式
     */
    private static final String POINT = "execution(*  cn.xdf.xadd.controller..*.*(..))";
    /**
     * 记录每次请求的开始时间
     */
    private static final ThreadLocal<Long> START_TIME = new ThreadLocal<>();
    /**
     * 记录每次请求的随机序列号
     */
    private static final ThreadLocal<String> SEQUENCE = new ThreadLocal<>();

    @Pointcut(POINT)
    public void executeService() {
        //empty point cut
    }

    @Before("executeService()")
    public void before(JoinPoint pjp) {
        //请求信息
        RequestAttributes ra = RequestContextHolder.getRequestAttributes();
        ServletRequestAttributes sra = (ServletRequestAttributes) ra;
        HttpServletRequest httpServletRequest = sra.getRequest();
        //获取客户端真实IP
        String realIp = RealIpUtils.getClientIp(httpServletRequest);
        //解析Http Header
        Enumeration names = httpServletRequest.getHeaderNames();
        StringBuilder header = new StringBuilder();
        while (names.hasMoreElements()) {
            String name = (String) names.nextElement();
            header.append(name).append(":").append(httpServletRequest.getHeader(name)).append("|");
        }
        //解析参数
        Object[] args = pjp.getArgs();
        Map<String, String[]> parameterMap = httpServletRequest.getParameterMap();
        StringBuilder stringBuilder = new StringBuilder();
        parameterMap.forEach((k, v) -> stringBuilder.append(k).append(":").append(Arrays.deepToString(v)).append("|"));

        //每次请求的序列号,避免方便匹配同一个请求以及对应的响应
        SEQUENCE.set(getRandom());
        //记录日志
        log.info("trace log begin --> sequence::{},userName::{},RealIp::{},User-Agent::{},requestUri::{}" +
                        ",header::{},requestMethod::{},request::{}, args::{}",
                SEQUENCE.get(), UserInfoContext.getUserName(), realIp, httpServletRequest.getHeader("User-Agent"),
                httpServletRequest.getRequestURI(), header.toString(), httpServletRequest.getMethod(),
                stringBuilder.toString(), Arrays.deepToString(args));
        //记录当前时间戳
        START_TIME.set(System.currentTimeMillis());
    }

    @AfterReturning(pointcut = "executeService()", returning = "result")
    public void after(Object result) {
        try {
            String res = "not standard response";
            //自定义标准返回类 也可以直接输出 不做判断
            if (result instanceof Result) {
                res = result.toString();
            }
            //记录返回信息
            log.info("trace log end --> sequence::{}, used::{}ms, responseBody::{}", SEQUENCE.get(), (System.currentTimeMillis() - START_TIME.get()), res);
        } finally {
            START_TIME.remove();
            SEQUENCE.remove();
        }
    }

    /**
     * 生成8位随机字符串
     *
     * @return java.lang.String
     * @date 2020-08-11 16:52
     */
    private String getRandom() {
        return RandomStringUtils.random(8, true, true);
    }
}


特点:

  • 主要记录了请求的header、参数、当前登陆用户的信息(可以去掉,用户相关的工具类没上传)
  • 每次请求与响应有唯一的序列号对应
  • 记录每次请求的耗时
  • 可针对自定义响应记录标准响应日志,其他的异常响应不做记录

其他待日后完善。

猜你喜欢

转载自blog.csdn.net/sinat_14840559/article/details/113700745
今日推荐