springboot使用自定義註解實現日誌記錄功能

   有這麼一個需求:記錄controller的請求參數和響應結果到日誌中。

   解決思路:寫一個自定義註解,在需要記錄的controller的方法上,加上該註解,通過註解來記錄相關信息。

   解決方式:使用AOP來解決,通過返回通知來獲取返回結果信息。

   解決步驟:

1、在pom.xml中引入AOP的依賴

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

2、定義枚舉類,針對方法參數制定枚舉類型

public enum ParamTypeEnum {
    PARAM_BEAN("bean","bean"),//bean類型
    PARAM_KAK_JSON("k1-v1,k2-v2,...,json","kak_json"),//k-v鍵值對和json串混合類型
    PARAM_KAK_MAP("k1-v1,k2-v2,...,map","kak_map"),//k-v鍵值對和map混合類型
    PARAM_KAK("k1-v1,k2-v2,...","kak"),//k-v鍵值對類型
    PARAM_MAP("map","map"),//map類型
    PARAM_JSON("json","json");//json類型

    private String name;
    private String code;

    public String getName() {
        return this.name;
    }

    public String getCode() {
        return this.code;
    }

    private ParamTypeEnum(String name, String code){
        this.name = name;
        this.code = code;
    }
}

3、定義自定義註解

//以下3個元註解的作用,自行百度即可
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ManagerLog {
    /**
     * 參數類型,一共6種
     * 1、json
     * 2、map
     * 3、K-V鍵值對
     * 4、K-V鍵值對 與 json 混合
     * 5、K-V鍵值對 與 map 混合
     * 6、bean
     * @return
     */
    ParamTypeEnum paramType();

    /**
     * 參數爲混合參數時使用,標記map/json所在的參數位置,從0開始計數
     * @return
     */
    int mulIndex() default -1;

    /**
     * json是否爲嵌套json,僅支持2級
     * @return
     */
    boolean mulJson() default false;

    /**
     * 舉例:{"data":{"a":1,"b":2}}
     * 嵌套json的key
     * @return
     */
    String childJsonName() default "data";
}

4、AOP實現

@Aspect
@Component
public class ManagerLogAspect {

    @Resource
    private KafkaManagerLogSenderUtil kafkaManagerLogSenderUtil;//將日誌信息發送到kafka

    @Pointcut("@annotation(com.chineseall.admin.annotation.ManagerLog)")
    public void annotationPointCut() {
    }

    @AfterReturning(returning = "result",value = "annotationPointCut()&&@annotation(managerLog)")
    public Object afterReturn(JoinPoint point, Object result, ManagerLog managerLog) {
        try{
            //獲取參數類型枚舉
            ParamTypeEnum paramTypeEnum = managerLog.paramType();
            //json
            if (ParamTypeEnum.PARAM_JSON.getCode().equals(paramTypeEnum.getCode())){
                String jsonStr = (String) point.getArgs()[0];
                dealJson(point,result,managerLog,jsonStr);
            }
            //map
            else if (ParamTypeEnum.PARAM_MAP.getCode().equals(paramTypeEnum.getCode())){
                dealMap(point,result,managerLog);
            }
            //k-v鍵值對
            else if (ParamTypeEnum.PARAM_KAK.getCode().equals(paramTypeEnum.getCode())){
                dealKak(point,result,managerLog);
            }
            //k-v鍵值對 與 json 混合
            else if (ParamTypeEnum.PARAM_KAK_JSON.getCode().equals(paramTypeEnum.getCode())){
                dealKakAndJson(point,result,managerLog);
            }
            //k-v鍵值對 與 map 混合
            else if (ParamTypeEnum.PARAM_KAK_MAP.getCode().equals(paramTypeEnum.getCode())){
                dealKakAndMap(point,result,managerLog);
            }
            else if (ParamTypeEnum.PARAM_BEAN.getCode().equals(paramTypeEnum.getCode())){
                String jsonStr = JSONObject.toJSONString(point.getArgs()[0]);
                dealJson(point,result,managerLog,jsonStr);
            }
        }catch (Exception e){
            System.out.println("ManagerLogAspect切面記錄日誌時發生錯誤!"+e.getMessage());
            log.error("ManagerLogAspect切面記錄日誌時發生錯誤!"+e.getMessage());
        }
        return result;
    }

    /**
     * 發送消息至kafka
     * @param desc
     * @throws Exception
     */
    private void sendMsg(String desc) throws Exception{
        

        kafkaManagerLogSenderUtil.sendManagerMsg(desc);
    }

   
    /**
     * 處理參數類型爲k1-v1,k2-v2,...,map的數據
     * @param point
     * @param result
     * @param managerLog
     * @throws Exception
     */
    private void dealKakAndMap(JoinPoint point, Object result, ManagerLog managerLog) throws Exception{
       
        int index = managerLog.mulIndex();
        if (index==-1){
            throw new Exception("在@ManagerLog註解中,未標明map的位置");
        }
        Object[] args = point.getArgs();
        String targetValue = null;
        Map<String,Object> map = (Map)point.getArgs()[index];
        String[] paramNames = getParamNames(point);

        StringBuilder sb = new StringBuilder();
        sb.append("#managerLog#\n執行了如下操作:\n");
        
        for (int i=0;i<args.length;i++){
                sb.append(paramNames[i]).append(":").append(args[i].toString()).append("\n");
            
        }
        if (map != null){
            for (String key : map.keySet()){
                sb.append(key).append(":").append(map.get(key).toString()).append("\n");
               
            }
        }
        //追加執行結果
        appendResult(sb,result,targetParamName);              
        sendMsg(sb.toString());
    }

    /**
     * 處理參數類型爲k1-v1,k2-v2,...,json的數據
     * @param point
     * @param result
     * @param managerLog
     * @throws Exception
     */
    private void dealKakAndJson(JoinPoint point, Object result, ManagerLog managerLog) throws Exception{        
        Object[] args = point.getArgs();
        String jsonStr = (String)args[index];
        JSONObject jsonObject = JSONObject.parseObject(jsonStr);
        if (managerLog.mulJson()){
            jsonObject = jsonObject.getJSONObject(managerLog.childJsonName());
        }
        String[] paramNames = getParamNames(point);

        StringBuilder sb = new StringBuilder();
        sb.append("#managerLog#\n執行了如下操作:\n");
        
        for (int i=0;i<args.length;i++){
              sb.append(paramNames[i]).append(":").append(args[i].toString()).append("\n");
            
        }

        if (jsonObject != null){
            for (Map.Entry<String,Object> entry : jsonObject.entrySet()){
                sb.append(entry.getKey()).append(":").append(entry.getValue()).append("\n");
            }
        }
        //追加執行結果
        appendResult(sb,result,targetParamName);
        
        sendMsg(sb.toString());
    }

    /**
     * 處理參數類型爲k-v的數據
     * @param point
     * @param result
     * @param managerLog
     */
    private void dealKak(JoinPoint point, Object result, ManagerLog managerLog) throws Exception{
        
        StringBuilder sb = new StringBuilder();
        sb.append("#managerLog#\n執行了如下操作:\n");
        
        Object[] args = point.getArgs();
        String[] paramNames = getParamNames(point);
        for (int i=0;i<args.length;i++){                     sb.append(paramNames[i]).append(":").append(args[i].toString()).append("\n");
        }
        appendResult(sb,result,targetParamName);
        
        
        sendMsg(sb.toString());
    }

    /**
     * 獲取參數名
     * @param point
     * @return
     */
    private String[] getParamNames(JoinPoint point){
        Signature signature = point.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        String[] paramNames = methodSignature.getParameterNames();
        if (paramNames == null){
            paramNames = new String[0];
        }
        return paramNames;
    }


    /**
     * 處理參數類型爲map的數據
     * @param point
     * @param result
     * @param managerLog
     */
    private void dealMap(JoinPoint point, Object result, ManagerLog managerLog) throws Exception{
        
        Map<String,Object> map = (Map)point.getArgs()[0];
        StringBuilder sb = new StringBuilder();
        sb.append("#managerLog#\n執行了如下操作:\n");
        
        if (map != null){
            for (String key : map.keySet()){
                sb.append(key).append(":").append(map.get(key).toString()).append("\n");
                
            }
        }
        //追加執行結果
        appendResult(sb,result,targetParamName);
        
        sendMsg(sb.toString());
    }


    /**
     * 處理參數類型爲json的數據
     * @param point
     * @param result
     * @param managerLog
     */
    private void dealJson(JoinPoint point, Object result, ManagerLog managerLog,String jsonStr) throws Exception{
        

        JSONObject jsonObject = JSONObject.parseObject(jsonStr);
        if (managerLog.mulJson()){
            jsonObject = jsonObject.getJSONObject(managerLog.childJsonName());
        }
        StringBuilder sb = new StringBuilder();
        sb.append("#managerLog#\n執行了如下操作:\n");
       
        if (jsonObject != null){
            for (Map.Entry<String,Object> entry : jsonObject.entrySet()){
                sb.append(entry.getKey()).append(":").append(entry.getValue()).append("\n");
                
            }
        }
        //追加執行結果
        appendResult(sb,result,targetParamName);
        
        
        sendMsg(sb.toString());
    }

    /**
     * 獲取執行結果
     * @param sb
     * @param result
     * @return
     */
    private String appendResult(StringBuilder sb,Object result){
        return appendResult(sb,result,null);
    }

//targetParamName和targetParamValue是業務相關的東西,不需要關注
    /**
     * 獲取執行結果
     * @param sb
     * @param result
     * @param targetParamName
     * @return
     */
    private String appendResult(StringBuilder sb,Object result,String targetParamName){
        String targetValue = "-1";
        try {
            sb.append("執行的結果如下:\n");
            ReturnMsg returnMsg = (ReturnMsg)result;
            if (null != returnMsg) {
                if (returnMsg.getCode() == 0) {
                    sb.append(returnMsg.getData());

                    if (targetParamName != null){
                        try {
                            //從執行結果中獲取targetParamName對應的值
                            targetValue = getTargetValue(returnMsg,targetParamName);
                        }catch (Exception e){
                            System.out.println("從執行結果中獲取targetParamName對應的值異常"+e.getMessage());
                            targetValue = "-1";
                        }
                    }
                } else {
                    sb.append(!StringUtils.isEmpty(returnMsg.getException()) ? returnMsg.getException() : returnMsg.getMsg());
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("獲取執行結果發生異常"+e.getMessage());
        }
        return targetValue;
    }

    /**
     * 從執行結果中獲取targetParamName對應的值
     * @param returnMsg
     * @param targetParamName
     * @return
     */
    private String getTargetValue(ReturnMsg returnMsg,String targetParamName){
        String targetValue = "0";
        String jsonString = JSONObject.toJSONString(returnMsg.getData());
        JSONObject jsonObject = JSONObject.parseObject(jsonString);
        for (Map.Entry<String,Object> entry : jsonObject.entrySet()){
            if (targetParamName.equals(entry.getKey())){
                targetValue = entry.getValue().toString();
            }
        }
        return targetValue;
    }
}

6、在controller的方法上添加該註解

    @ManagerLog(paramType = ParamTypeEnum.PARAM_JSON)
    @PostMapping("/single")
    public ReturnMsg<Object> singleBook(@RequestBody String body) throws Exception{
    }

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章