天天看點

【工具類】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、參數、目前登陸使用者的資訊(可以去掉,使用者相關的工具類沒上傳)
  • 每次請求與響應有唯一的序列号對應
  • 記錄每次請求的耗時
  • 可針對自定義響應記錄标準響應日志,其他的異常響應不做記錄

其他待日後完善。