自定义注解+Spring AOP实现记录项目日志

256 阅读4分钟

前言

在项目中,我们需要关注日志,来看异常以及哪里会出现的问题,并且需要记录来访的ip等一些信息,用于数据统计,最近封装了一套,下面来介绍下。

正文

一、数据库设计

正常日志表

	
CREATE TABLE `tb_exc_log`  (


	
  `id` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,


	
  `create_time` datetime(0) NULL DEFAULT NULL,


	
  `modified_time` datetime(0) NULL DEFAULT NULL,


	
  `deleted` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '0',


	
  `params` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,


	
  `method_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,


	
  `exc_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,


	
  `exc_message` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,


	
  `uri` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,


	
  `ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,


	
  `module` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,


	
  `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,


	
  `api_desc` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,


	
  PRIMARY KEY (`id`) USING BTREE


	
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

异常日志表

	
CREATE TABLE `tb_request_log`  (


	
  `id` varchar(60) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL,


	
  `create_time` datetime(0) NULL DEFAULT NULL,


	
  `modified_time` datetime(0) NULL DEFAULT NULL,


	
  `deleted` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT '0',


	
  `ip` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,


	
  `uri` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,


	
  `params` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,


	
  `result` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,


	
  `module` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,


	
  `type` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,


	
  `api_desc` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL,


	
  PRIMARY KEY (`id`) USING BTREE


	
) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic;

二、实体类

对应上述数据库表的实体类

正常日志

import com.baomidou.mybatisplus.annotation.TableName;


	
import io.zzm.dragon.model.comm.BaseEntity;


	
import lombok.Data;


	
 


	
/**


	
 * @Author i8023tp


	
 **/


	
@Data


	
@TableName("tb_request_log")


	
public class ReqLog extends BaseEntity {


	
 


	
    private String ip;


	
 


	
    private String uri;


	
 


	
    private String params;


	
 


	
    private String result;


	
 


	
    private String module;


	
 


	
    private String type;


	
 


	
    private String apiDesc;


	
}


	
 

异常日志

	
import com.baomidou.mybatisplus.annotation.TableName;


	
import io.zzm.dragon.model.comm.BaseEntity;


	
import lombok.Data;


	
 


	
/**


	
 * @Author i8023tp


	
 **/


	
@Data


	
@TableName("tb_exc_log")


	
public class ExcLog  extends BaseEntity {


	
 


	
    private String params;


	
 


	
    private String methodName;


	
 


	
    private String excName;


	
 


	
    private String excMessage;


	
 


	
    private String uri;


	
 


	
    private String ip;


	
 


	
    private String module;


	
 


	
    private String type;


	
 


	
    private String apiDesc;


	
}


	
 

三、核心方法

自定义注解

	


	
 


	
import java.lang.annotation.*;


	
 


	
/**


	
 * @author i8023tp


	
 */


	
@Documented


	
@Target(ElementType.METHOD)


	
@Retention(RetentionPolicy.RUNTIME)


	
public @interface DragonLog {


	
 


	
    String desc() default "";


	
 


	
    String type() default  "";


	
 


	
    String module() default "";


	
 


	
}

日志操作接口

import org.springframework.boot.logging.LogLevel;


	
 


	
/**


	
 * @author i8023tp


	
 */


	
public interface LogService<T> {


	
 


	
    /**


	
     * @param log


	
     * @param logLevel


	
     */


	
    void printLog(T log, LogLevel logLevel);


	
 


	
    /**


	
     *


	
     * @param log


	
     * @param logLevel


	
     */


	
    void saveLog(T log,LogLevel logLevel);


	
 


	
}

日志操作实现类

import com.google.gson.ExclusionStrategy;


	
import com.google.gson.FieldAttributes;


	
import com.google.gson.GsonBuilder;


	
import io.zzm.dragon.model.domain.ExcLog;


	
import io.zzm.dragon.service.ExcLogService;


	
import lombok.extern.slf4j.Slf4j;


	
import org.springframework.boot.logging.LogLevel;


	
import org.springframework.scheduling.annotation.Async;


	
import org.springframework.stereotype.Component;


	
import org.springframework.stereotype.Service;


	
import org.springframework.web.multipart.MultipartFile;


	
 


	
import javax.annotation.Resource;


	
 


	
/**


	
 * @Author i8023tp


	
 **/


	
@Service("excOptionLogService")


	
@Slf4j


	
@Component


	
public class ExcLogServiceImpl implements LogService<ExcLog> {


	
 


	
    @Resource


	
    private ExcLogService excLogService;


	
 


	
    /**


	
     * @param excLog


	
     * @param logLevel


	
     */


	
    @Override


	
    @Async


	
    public void printLog(ExcLog excLog, LogLevel logLevel) {


	
        String result = new GsonBuilder().disableHtmlEscaping().setExclusionStrategies(new ExclusionStrategy() {


	
            @Override


	
            public boolean shouldSkipField(FieldAttributes fieldAttributes) {


	
                return fieldAttributes.getDeclaredType() instanceof MultipartFile;


	
            }


	
 


	
            @Override


	
            public boolean shouldSkipClass(Class<?> aClass) {


	
                return false;


	
            }


	
        }).create().toJson(excLog);


	
        log.error(result);


	
    }


	
 


	
    /**


	
     * @param log


	
     * @param logLevel


	
     */


	
    @Override


	
    @Async


	
    public void saveLog(ExcLog excLog, LogLevel logLevel) {


	
        excLogService.save(excLog);


	
    }


	
}


	
 
	
import com.google.gson.ExclusionStrategy;


	
import com.google.gson.FieldAttributes;


	
import com.google.gson.GsonBuilder;


	
import io.zzm.dragon.model.domain.ReqLog;


	
import io.zzm.dragon.service.ReqLogService;


	
import lombok.extern.slf4j.Slf4j;


	
import org.springframework.boot.logging.LogLevel;


	
import org.springframework.scheduling.annotation.Async;


	
import org.springframework.stereotype.Component;


	
import org.springframework.stereotype.Service;


	
import org.springframework.web.multipart.MultipartFile;


	
 


	
import javax.annotation.Resource;


	
 


	
/**


	
 * @Author i8023tp


	
 **/


	
@Service("normalLogService")


	
@Slf4j


	
@Component


	
public class NormalLogServiceImpl implements LogService<ReqLog> {


	
 


	
    @Resource


	
    private ReqLogService reqLogService;


	
 


	
    /**


	
     * @param reqLog


	
     * @param logLevel


	
     */


	
    @Override


	
    @Async


	
    public void printLog(ReqLog reqLog, LogLevel logLevel) {


	
        String result = new GsonBuilder().disableHtmlEscaping().setExclusionStrategies(new ExclusionStrategy() {


	
            @Override


	
            public boolean shouldSkipField(FieldAttributes fieldAttributes) {


	
                return fieldAttributes.getDeclaredType() instanceof MultipartFile;


	
            }


	
 


	
            @Override


	
            public boolean shouldSkipClass(Class<?> aClass) {


	
                return false;


	
            }


	
        }).create().toJson(reqLog);


	
        if (logLevel == LogLevel.ERROR){


	
            log.error(result);


	
        }else {


	
            log.info(result);


	
        }


	
    }


	
 


	
    /**


	
     * @param log


	
     * @param logLevel


	
     */


	
    @Override


	
    @Async


	
    public void saveLog(ReqLog log, LogLevel logLevel) {


	
        reqLogService.save(log);


	
    }


	
}


	
 

aop实现切面

	



	
import java.lang.reflect.Method;


	
import java.time.LocalDateTime;


	
 


	
import com.google.gson.Gson;


	
import io.zzm.dragon.anno.DragonLog;


	
import io.zzm.dragon.model.domain.ExcLog;


	
import io.zzm.dragon.model.domain.ReqLog;


	
import io.zzm.dragon.utils.IpUtils;


	
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.beans.factory.annotation.Autowired;


	
import org.springframework.beans.factory.annotation.Qualifier;


	
import org.springframework.boot.logging.LogLevel;


	
import org.springframework.stereotype.Component;


	
import org.springframework.web.context.request.RequestAttributes;


	
import org.springframework.web.context.request.RequestContextHolder;


	
 


	
import javax.annotation.Resource;


	
import javax.servlet.http.HttpServletRequest;


	
 


	
/**


	
 * 此方法为日志的核心方法,使用aop进行环绕


	
 * @author i8023tp


	
 **/


	
@Aspect


	
@Component


	
@Slf4j


	
public class DragonLogAspect {


	
 


	
    @Qualifier("excOptionLogService")


	
    @Resource


	
    private LogService<ExcLog> excOptionLogService;


	
 


	
    @Qualifier("normalLogService")


	
    @Resource


	
    private LogService<ReqLog> normalLogService;


	
 


	
    /**


	
     * 定义切点


	
     */


	
    @Pointcut("@annotation(io.zzm.dragon.anno.DragonLog)")


	
    public void normalLogPointCut(){


	
    }


	
 


	
    /**


	
     * 定义切点


	
     */


	
    @Pointcut("execution(* io.zzm.dragon.controller..*.*(..))")


	
    public void excLogPointCut(){


	
    }


	
 


	
 


	
    @AfterReturning(value = "normalLogPointCut()",returning = "keys")


	
    public void saveNormalLog(JoinPoint joinPoint,Object keys){


	
        // 获取RequestAttributes


	
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();


	
        // 从获取RequestAttributes中获取HttpServletRequest的信息


	
        HttpServletRequest request = (HttpServletRequest) requestAttributes


	
                .resolveReference(RequestAttributes.REFERENCE_REQUEST);


	
 


	
        ReqLog reqLog = new ReqLog();


	
        try{


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


	
            Method method = signature.getMethod();


	
            DragonLog annotation = method.getAnnotation(DragonLog.class);


	
            if (annotation !=null){


	
                reqLog.setModule(annotation.module());


	
                reqLog.setType(annotation.type());


	
                reqLog.setApiDesc(annotation.desc());


	
            }


	
 


	
            reqLog.setIp(IpUtils.getIpAddr(request));


	
            reqLog.setUri(request.getRequestURI());


	
            reqLog.setParams(new Gson().toJson(request.getParameterMap()));


	
            reqLog.setResult(new Gson().toJson(keys));


	
            reqLog.setCreateTime(LocalDateTime.now());


	
            reqLog.setModifiedTime(LocalDateTime.now());


	
            normalLogService.printLog(reqLog,LogLevel.INFO);


	
            normalLogService.saveLog(reqLog, LogLevel.INFO);


	
        }catch (Exception e){


	
            log.error(e.getMessage());


	
        }


	
 


	
    }


	
 


	
    @AfterThrowing(pointcut = "excLogPointCut()",throwing = "t")


	
    public void saveExcLog(JoinPoint joinPoint,Throwable t){


	
        // 获取RequestAttributes


	
        RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();


	
        // 从获取RequestAttributes中获取HttpServletRequest的信息


	
        HttpServletRequest request = (HttpServletRequest) requestAttributes


	
                .resolveReference(RequestAttributes.REFERENCE_REQUEST);


	
        ExcLog excLog = new ExcLog();


	
        excLog.setId("");


	
        excLog.setCreateTime(LocalDateTime.now());


	
        excLog.setModifiedTime(LocalDateTime.now());


	
        try{


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


	
            Method method = signature.getMethod();


	
            excLog.setMethodName(joinPoint.getTarget().getClass().getName()+"."+method.getName());


	
            excLog.setParams(new Gson().toJson(request.getParameterMap()));


	
            excLog.setUri(request.getRequestURI());


	
            excLog.setIp(IpUtils.getIpAddr(request));


	
            DragonLog annotation = method.getAnnotation(DragonLog.class);


	
            if (annotation!=null){


	
                excLog.setModule(annotation.module());


	
                excLog.setType(annotation.type());


	
                excLog.setApiDesc(annotation.desc());


	
            }


	
            excLog.setExcName(t.getClass().getName());


	
            excLog.setExcMessage(t.getMessage());


	
            excOptionLogService.printLog(excLog,LogLevel.ERROR);


	
            excOptionLogService.saveLog(excLog,LogLevel.ERROR);


	
        }catch (Exception e){


	
            log.error(e.getMessage());


	
        }


	
    }


	
 


	
}

结语

使用上述代码,即可做到记录日志打印控制台,并存入数据库。