日志切面功能整合
This commit is contained in:
51
cash-common/cash-common-log/pom.xml
Normal file
51
cash-common/cash-common-log/pom.xml
Normal file
@@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
|
||||
<parent>
|
||||
<groupId>com.czg</groupId>
|
||||
<artifactId>cash-common</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>cash-common-log</artifactId>
|
||||
<packaging>jar</packaging>
|
||||
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>com.czg</groupId>
|
||||
<artifactId>cash-common-tools</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.czg</groupId>
|
||||
<artifactId>cash-common-redis</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.czg</groupId>
|
||||
<artifactId>cash-common-sa-token</artifactId>
|
||||
<version>${project.version}</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.commons</groupId>
|
||||
<artifactId>commons-lang3</artifactId>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.mybatis-flex</groupId>
|
||||
<artifactId>mybatis-flex-spring-boot3-starter</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
|
||||
<build>
|
||||
<finalName>${project.artifactId}</finalName>
|
||||
</build>
|
||||
|
||||
</project>
|
||||
@@ -0,0 +1,62 @@
|
||||
package com.czg.log;
|
||||
|
||||
import cn.hutool.core.exceptions.ExceptionUtil;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.czg.config.RedisCst;
|
||||
import com.czg.service.RedisService;
|
||||
import com.mybatisflex.core.row.Db;
|
||||
import com.mybatisflex.core.row.Row;
|
||||
import jakarta.annotation.Resource;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.concurrent.BasicThreadFactory;
|
||||
import org.springframework.boot.CommandLineRunner;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.concurrent.ScheduledExecutorService;
|
||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
|
||||
/**
|
||||
* 从Redis队列中获取Log,保存到DB
|
||||
*
|
||||
* @author admin admin@cashier.com
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Slf4j
|
||||
@Component
|
||||
public class LogConsumer implements CommandLineRunner {
|
||||
@Resource
|
||||
private RedisService redisService;
|
||||
private final ScheduledExecutorService scheduledService = new ScheduledThreadPoolExecutor(1,
|
||||
new BasicThreadFactory.Builder().namingPattern("log-consumer-schedule-pool-%d").daemon(true).build());
|
||||
|
||||
@Override
|
||||
public void run(String... args) {
|
||||
//上次任务结束后,等待10秒钟,再执行下次任务
|
||||
scheduledService.scheduleWithFixedDelay(() -> {
|
||||
try {
|
||||
receiveQueue();
|
||||
} catch (Exception e) {
|
||||
log.error("LogConsumer Error:" + ExceptionUtil.stacktraceToString(e));
|
||||
}
|
||||
}, 1, 10, TimeUnit.SECONDS);
|
||||
}
|
||||
|
||||
private void receiveQueue() {
|
||||
String key = RedisCst.SYS_LOG_KEY;
|
||||
//每次插入100条
|
||||
int count = 100;
|
||||
for (int i = 0; i < count; i++) {
|
||||
String log = (String) redisService.rightPop(key);
|
||||
if (log == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 操作日志入库
|
||||
Row row = JSON.parseObject(log, Row.class);
|
||||
Db.insert("operation_log",row);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
|
||||
|
||||
package com.czg.log.annotation;
|
||||
|
||||
import java.lang.annotation.*;
|
||||
|
||||
/**
|
||||
* 操作日志注解
|
||||
*
|
||||
* @author admin admin@cashier.com
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface LogOperation {
|
||||
String value() default "";
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
package com.czg.log.aspect;
|
||||
|
||||
|
||||
import cn.hutool.core.exceptions.ExceptionUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.extra.servlet.JakartaServletUtil;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
import com.alibaba.fastjson2.JSON;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import com.czg.log.annotation.LogOperation;
|
||||
import com.czg.log.enums.LogTypeEnum;
|
||||
import com.czg.log.enums.OperationStatusEnum;
|
||||
import com.czg.log.producer.LogProducer;
|
||||
import com.czg.sa.StpKit;
|
||||
import com.czg.utils.AddressUtil;
|
||||
import com.czg.utils.HttpContextUtil;
|
||||
import com.mybatisflex.core.keygen.impl.SnowFlakeIDKeyGenerator;
|
||||
import com.mybatisflex.core.row.Row;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.annotation.Pointcut;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.http.HttpHeaders;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 操作日志,切面处理类
|
||||
*
|
||||
* @author admin admin@cashier.com
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
public class LogOperationAspect {
|
||||
|
||||
@Resource
|
||||
private LogProducer logProducer;
|
||||
|
||||
@Pointcut("@annotation(com.czg.log.annotation.LogOperation)")
|
||||
public void logPointCut() {
|
||||
|
||||
}
|
||||
|
||||
@Around("logPointCut()")
|
||||
public Object around(ProceedingJoinPoint point) throws Throwable {
|
||||
long beginTime = System.currentTimeMillis();
|
||||
try {
|
||||
//执行方法
|
||||
Object result = point.proceed();
|
||||
|
||||
//执行时长(毫秒)
|
||||
long time = System.currentTimeMillis() - beginTime;
|
||||
//保存日志
|
||||
saveLog(point, time, OperationStatusEnum.SUCCESS.value());
|
||||
|
||||
return result;
|
||||
} catch (Exception e) {
|
||||
//执行时长(毫秒)
|
||||
long time = System.currentTimeMillis() - beginTime;
|
||||
String errorInfo = ExceptionUtil.stacktraceToString(e);
|
||||
//保存日志
|
||||
saveLog(point, time, OperationStatusEnum.FAIL.value(), errorInfo);
|
||||
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
|
||||
private void saveLog(ProceedingJoinPoint joinPoint, long time, Integer status) {
|
||||
saveLog(joinPoint, time, status, null);
|
||||
}
|
||||
|
||||
|
||||
private void saveLog(ProceedingJoinPoint joinPoint, long time, Integer status, String errorInfo) {
|
||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||
Method method = signature.getMethod();
|
||||
|
||||
Row log = new Row();
|
||||
LogOperation annotation = method.getAnnotation(LogOperation.class);
|
||||
if (annotation != null) {
|
||||
//注解上的描述
|
||||
log.set("operation", annotation.value());
|
||||
}
|
||||
|
||||
//登录用户信息
|
||||
Long shopId = StpKit.USER.getShopId();
|
||||
Long createUserId = StpKit.USER.getLoginIdAsLong();
|
||||
//TODO SA-TOKEN 暂未整合当前登录人信息,此处仅为临时账号
|
||||
String createUserName = "temp-account";
|
||||
|
||||
log.set("createUserId", createUserId);
|
||||
log.set("createUserName", createUserName);
|
||||
log.set("shopId", shopId);
|
||||
|
||||
log.set("type", LogTypeEnum.INFO.value());
|
||||
if(StrUtil.isNotBlank(errorInfo)){
|
||||
log.set("type", LogTypeEnum.ERROR.value());
|
||||
}
|
||||
log.set("status", status);
|
||||
log.set("requestTime", (int) time);
|
||||
log.set("createTime", LocalDateTime.now());
|
||||
|
||||
//请求相关信息
|
||||
HttpServletRequest request = HttpContextUtil.getHttpServletRequest();
|
||||
String ip = JakartaServletUtil.getClientIP(request);
|
||||
log.set("ip", ip);
|
||||
log.set("location", AddressUtil.getRealAddressByIp(ip));
|
||||
log.set("userAgent", request.getHeader(HttpHeaders.USER_AGENT));
|
||||
log.set("requestUri", request.getRequestURI());
|
||||
log.set("requestMethod", request.getMethod());
|
||||
log.set("errorInfo", errorInfo);
|
||||
//请求参数
|
||||
Object[] args = joinPoint.getArgs();
|
||||
Map<String, String> param = JakartaServletUtil.getParamMap(request);
|
||||
try {
|
||||
String params = JSON.toJSONString(args[0]);
|
||||
log.set("requestParams", params);
|
||||
boolean isJsonObject = JSONUtil.isTypeJSONObject(params);
|
||||
if(isJsonObject){
|
||||
JSONObject reqData = JSON.parseObject(params);
|
||||
param.forEach(reqData::putIfAbsent);
|
||||
log.set("requestParams", reqData.toJSONString());
|
||||
}
|
||||
} catch (Exception ignored) {
|
||||
|
||||
}
|
||||
log.set("id", new SnowFlakeIDKeyGenerator().nextId());
|
||||
//保存到Redis队列里
|
||||
logProducer.saveLog(JSON.toJSONString(log.toUnderlineKeysMap()));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.czg.log.enums;
|
||||
|
||||
/**
|
||||
* 日志类型枚举
|
||||
*
|
||||
* @author admin admin@cashier.com
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public enum LogTypeEnum {
|
||||
/**
|
||||
* INFO
|
||||
*/
|
||||
INFO("info"),
|
||||
/**
|
||||
* ERROR
|
||||
*/
|
||||
ERROR("error");
|
||||
|
||||
private final String value;
|
||||
|
||||
LogTypeEnum(String value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public String value() {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.czg.log.enums;
|
||||
|
||||
/**
|
||||
* 操作状态枚举
|
||||
*
|
||||
* @author admin admin@cashier.com
|
||||
* @since 1.0.0
|
||||
*/
|
||||
public enum OperationStatusEnum {
|
||||
/**
|
||||
* 失败
|
||||
*/
|
||||
FAIL(0),
|
||||
/**
|
||||
* 成功
|
||||
*/
|
||||
SUCCESS(1);
|
||||
|
||||
private final int value;
|
||||
|
||||
OperationStatusEnum(int value) {
|
||||
this.value = value;
|
||||
}
|
||||
|
||||
public int value() {
|
||||
return this.value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.czg.log.producer;
|
||||
|
||||
import cn.hutool.core.thread.ThreadFactoryBuilder;
|
||||
import com.czg.config.RedisCst;
|
||||
import com.czg.service.RedisService;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.util.concurrent.*;
|
||||
|
||||
/**
|
||||
* 日志通过redis队列,异步保存到数据库
|
||||
*
|
||||
* @author admin admin@cashier.com
|
||||
* @since 1.0.0
|
||||
*/
|
||||
@Component
|
||||
public class LogProducer {
|
||||
@Resource
|
||||
private RedisService redisService;
|
||||
|
||||
ThreadFactory namedThreadFactory = new ThreadFactoryBuilder().setNamePrefix("log-producer-pool").build();
|
||||
|
||||
ExecutorService pool = new ThreadPoolExecutor(5, 200, 0L, TimeUnit.MILLISECONDS,
|
||||
new LinkedBlockingQueue<>(1024), namedThreadFactory, new ThreadPoolExecutor.AbortPolicy());
|
||||
|
||||
/**
|
||||
* 保存Log到Redis消息队列
|
||||
*/
|
||||
public void saveLog(String log) {
|
||||
String key = RedisCst.SYS_LOG_KEY;
|
||||
|
||||
//异步保存到队列
|
||||
pool.execute(() -> redisService.leftPush(key, log, RedisService.NOT_EXPIRE));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user