前言
在项目中,我们需要关注日志,来看异常以及哪里会出现的问题,并且需要记录来访的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());
}
}
}
结语
使用上述代码,即可做到记录日志打印控制台,并存入数据库。