前言

在项目中,我们需要关注日志,来看异常以及哪里会出现的问题,并且需要记录来访的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;

二、实体类

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

正常日志

package io.zzm.dragon.model.domain;

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;
}

异常日志

package io.zzm.dragon.model.domain;

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;
}

三、核心方法

自定义注解

package io.zzm.dragon.anno;

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 "";

}

日志操作接口

package io.zzm.dragon.core;


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);

}

日志操作实现类

package io.zzm.dragon.core;

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);
    }
}

package io.zzm.dragon.core;

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实现切面

package io.zzm.dragon.core;
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());
        }
    }

}

结语

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

Q.E.D.


Hello welcome to my blog