1. 程式人生 > 實用技巧 >基於Aop實現記錄操作日誌和異常資訊

基於Aop實現記錄操作日誌和異常資訊

1.自定義註解

/**
 * @Author ZhengQinfeng
 * @Date 2020/10/31 16:33
 * @dec
 */

import java.lang.annotation.*;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface OperLog {
    /**
     * 操作模組
     *
     * @return
     */
    String operModule() default "";

    /**
     * 操作型別
     *
     * @return
     */
    String operType() default "";

    /**
     * 操作描述
     *
     * @return
     */
    String operDesc() default "";
}

2.自定義切面

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import qinfeng.zheng.anno.OperLog;
import qinfeng.zheng.util.IPUtil;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

/**
 * @Author ZhengQinfeng
 * @Date 2020/10/31 16:33
 * @dec
 */
@Slf4j
@Aspect
@Component
public class OperLogAspect {


    /**
     * 定義日誌操作切入點
     */
    @Pointcut("@annotation(qinfeng.zheng.anno.OperLog)")
    public void operLogPointCut() {

    }


    /**
     * 切入點是controller包下的所有方法
     */
    @Pointcut("execution(* qinfeng.zheng.controller..*.*(..))")
    public void exceptionLogPointCut() {

    }

    /**
     * 正常返回通知,如果切入點丟擲異常,不會執行
     *
     * @param joinPoint 切入點
     * @param response  返回結果
     */
    @AfterReturning(value = "operLogPointCut()", returning = "response")
    public void saveOperLog(JoinPoint joinPoint, Object response) {
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);


        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 切入點方法
        Method method = signature.getMethod();


        OperLog operLog = method.getAnnotation(OperLog.class);
        String module = "";
        String type = "";
        String desc = "";
        if (operLog != null) {
            module = operLog.operModule();
            type = operLog.operType();
            desc = operLog.operDesc();
        }

        String className = joinPoint.getTarget().getClass().getSimpleName();


        String methodName = method.getName();


        methodName = className + "." + methodName;

        // 請求URI
        String requestURI = request.getRequestURI();

        // 操作ip
        String operIp = IPUtil.getIpFromRequest(request);

        // 請求引數
        Object[] args = joinPoint.getArgs();

        log.info("模組:{}, 型別:{}, 描述:{},方法:{}, 入參:{}, uri:{}, 請求ip:{}, 響應值:{}", module, type, desc, methodName, args, requestURI, operIp, response);

    }

    @AfterThrowing(pointcut = "exceptionLogPointCut()", throwing = "error")
    public void saveException(JoinPoint joinPoint, Throwable error) {

        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = (HttpServletRequest) requestAttributes.resolveReference(RequestAttributes.REFERENCE_REQUEST);

        try {

            MethodSignature signature = (MethodSignature) joinPoint.getSignature();

            Method method = signature.getMethod();

            String className = joinPoint.getTarget().getClass().getSimpleName();


            String methodName = method.getName();


            methodName = className + "." + methodName;

            // 請求URI
            String requestURI = request.getRequestURI();

            // 操作ip
            String operIp = IPUtil.getIpFromRequest(request);

            // 請求引數
            Object[] args = joinPoint.getArgs();

            // 獲取異常名稱
            String exceptionName = error.getClass().getName();

            // 異常資訊
            String errorMessage = stackTraceToString(exceptionName, error.getMessage(), error.getStackTrace());


            log.info("方法:{}, 入參:{}, uri:{}, 請求ip:{}, 異常資訊:{}", methodName, args, requestURI, operIp, errorMessage);
            
        } catch (Exception e) {
            log.error("{}", e.getMessage(), e);
        }


    }

    public String stackTraceToString(String exceptionName, String exceptionMessage, StackTraceElement[] stackTraceElements) {
        StringBuffer stringBuffer = new StringBuffer();
        for (StackTraceElement traceElement : stackTraceElements) {
            stringBuffer.append(traceElement).append("\n");
        }
        String message = exceptionName + ":" + exceptionMessage + "\n\t" + stringBuffer.toString();
        return message;
    }


}

3.Ip工具類

import org.apache.commons.lang.text.StrTokenizer;

import javax.servlet.http.HttpServletRequest;
import java.util.regex.Pattern;

/**
 * IpV4 獲取真實ip地址
 */
public class IPUtil {

    public static final String _255 = "(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)";
    public static final Pattern pattern = Pattern.compile("^(?:" + _255 + "\\.){3}" + _255 + "$");

    /**
     * String型別ip轉為Long型別
     * 
     * @param longIp
     * @return String
     */
    public static String longToIpV4(long longIp) {
        int octet3 = (int) ((longIp >> 24) % 256);
        int octet2 = (int) ((longIp >> 16) % 256);
        int octet1 = (int) ((longIp >> 8) % 256);
        int octet0 = (int) ((longIp) % 256);
        return octet3 + "." + octet2 + "." + octet1 + "." + octet0;
    }

    /**
     * Long型別ip轉為String型別
     * 
     * @param ip
     * @return Long
     */
    public static long ipV4ToLong(String ip) {
        String[] octets = ip.split("\\.");
        return (Long.parseLong(octets[0]) << 24) + (Integer.parseInt(octets[1]) << 16)
                + (Integer.parseInt(octets[2]) << 8) + Integer.parseInt(octets[3]);
    }

    /**
     * @param ip
     * @return boolean
     */
    public static boolean isIPv4Private(String ip) {
        long longIp = ipV4ToLong(ip);
        return (longIp >= ipV4ToLong("10.0.0.0") && longIp <= ipV4ToLong("10.255.255.255"))
                || (longIp >= ipV4ToLong("172.16.0.0") && longIp <= ipV4ToLong("172.31.255.255"))
                || longIp >= ipV4ToLong("192.168.0.0") && longIp <= ipV4ToLong("192.168.255.255");
    }

    public static boolean isIPv4Valid(String ip) {
        return pattern.matcher(ip).matches();
    }

    /**
     * 獲取String型別真實ip地址,基於反向代理。
     * 
     * @param request
     * @return
     * 在反向代理中將X-Forward-For替換為remote_addr,即,真實的IP地址。
     */
    public static String getIpFromRequest(HttpServletRequest request) {
        String ip;
        boolean found = false;
        if ((ip = request.getHeader("x-forwarded-for")) != null) {
            StrTokenizer tokenizer = new StrTokenizer(ip, ",");
            while (tokenizer.hasNext()) {
                ip = tokenizer.nextToken().trim();
                if (isIPv4Valid(ip) && !isIPv4Private(ip)) {
                    found = true;
                    break;
                }
            }
        }
        if (!found) {
            ip = request.getRemoteAddr();// 獲得ip地址
        }
        return ip;
    }
}

4.測試類

import org.springframework.web.bind.annotation.*;
import qinfeng.zheng.anno.OperLog;
import qinfeng.zheng.vo.UserInfo;

import java.util.ArrayList;
        import java.util.List;

/**
 * @Author ZhengQinfeng
 * @Date 2020/10/31 20:16
 * @dec
 */
@RestController
public class TestController {

    @OperLog(operModule = "測試模組", operType = "新增", operDesc = "新增使用者")
    @PostMapping("/index")
    public List<UserInfo> index(@RequestBody UserInfo userInfo) {
        List<UserInfo> rets = new ArrayList<>();
        rets.add(userInfo);
        rets.add(userInfo);
        return rets;
    }

    @OperLog(operModule = "測試模組", operType = "錯誤資訊", operDesc = "測試異常列印資訊")
    @GetMapping("/error/{number}")
    public Integer index(@PathVariable Integer number) {
        return 10 / number;
    }
}