feat: 初始化
This commit is contained in:
+40
@@ -0,0 +1,40 @@
|
||||
package com.yida.data.log.annotation;
|
||||
|
||||
import com.yida.data.common.core.common.ModuleName;
|
||||
import com.yida.data.common.core.entity.system.enums.OperationLogTypeEnum;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
|
||||
/**
|
||||
* 后台操作日志注解
|
||||
*
|
||||
* @author ZYJ
|
||||
* @date 2021/1/7
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
public @interface OperationLog {
|
||||
|
||||
/**
|
||||
* 查询模块
|
||||
*/
|
||||
ModuleName module();
|
||||
|
||||
/**
|
||||
* 方法名称
|
||||
*/
|
||||
String methods() default "";
|
||||
|
||||
/**
|
||||
* 日志操作类型
|
||||
*
|
||||
* @see OperationLogTypeEnum
|
||||
*/
|
||||
OperationLogTypeEnum type() default OperationLogTypeEnum.SELECT;
|
||||
}
|
||||
+134
@@ -0,0 +1,134 @@
|
||||
package com.yida.data.log.aspect;
|
||||
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.yida.data.common.core.entity.CurrentUser;
|
||||
import com.yida.data.common.core.exception.FebsException;
|
||||
import com.yida.data.common.core.utils.FebsUtil;
|
||||
import com.yida.data.log.service.LogService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
import org.aspectj.lang.Signature;
|
||||
import org.aspectj.lang.annotation.Around;
|
||||
import org.aspectj.lang.annotation.Aspect;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.context.request.RequestContextHolder;
|
||||
import org.springframework.web.context.request.ServletRequestAttributes;
|
||||
import org.springframework.web.multipart.MultipartFile;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* 操作日志切面类
|
||||
*
|
||||
* @author ZYJ
|
||||
* @date 2021/1/8
|
||||
*/
|
||||
@Aspect
|
||||
@Component
|
||||
@Slf4j
|
||||
@EnableAsync
|
||||
@RequiredArgsConstructor
|
||||
public class OperationLogAspect {
|
||||
|
||||
private final LogService logService;
|
||||
|
||||
/**
|
||||
* 处理操作日志数据
|
||||
*
|
||||
* @param joinPoint ProceedingJoinPoint类
|
||||
* @return java.lang.Object
|
||||
* @author ZYJ
|
||||
* @date 2021/3/19 11:06
|
||||
*/
|
||||
@Around("@annotation(com.yida.data.log.annotation.OperationLog)")
|
||||
public Object insertOperationLog(ProceedingJoinPoint joinPoint) throws Throwable {
|
||||
//返回结果
|
||||
Object result;
|
||||
HttpServletRequest request = ((ServletRequestAttributes) Objects
|
||||
.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
|
||||
//请求参数
|
||||
String params = getRequestParams(joinPoint, request);
|
||||
//执行接口
|
||||
result = joinPoint.proceed();
|
||||
//异步保存日志
|
||||
Long createId = null;
|
||||
String createName = null;
|
||||
if (FebsUtil.isLogin()) {
|
||||
CurrentUser currentUser = FebsUtil.getCurrentUser();
|
||||
if (currentUser != null) {
|
||||
createId = currentUser.getUserId();
|
||||
createName = currentUser.getUsername();
|
||||
}
|
||||
}
|
||||
logService.saveOperationLog(joinPoint, FebsUtil.getHttpServletRequestIpAddress(), params, result, createId,
|
||||
createName);
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取请求参数
|
||||
*
|
||||
* @param joinPoint ProceedingJoinPoint类
|
||||
* @param request HttpServletRequest类
|
||||
* @return java.lang.String
|
||||
* @author ZYJ
|
||||
* @date 2021/1/11 14:21
|
||||
*/
|
||||
private static String getRequestParams(ProceedingJoinPoint joinPoint, HttpServletRequest request) {
|
||||
//得到请求参数值
|
||||
Object[] args = joinPoint.getArgs();
|
||||
|
||||
//获取请求头
|
||||
String contentType = request.getContentType();
|
||||
//判断是否为application/json请求
|
||||
if (StrUtil.isNotBlank(contentType) && (MediaType.APPLICATION_JSON_VALUE.equalsIgnoreCase(contentType) || contentType
|
||||
.equalsIgnoreCase(MediaType.APPLICATION_JSON_VALUE))) {
|
||||
//设置请求参数
|
||||
List<Object> objList = new ArrayList<>();
|
||||
//循环获取参数值
|
||||
for (Object arg : args) {
|
||||
if (arg instanceof HttpServletRequest
|
||||
|| arg instanceof HttpServletResponse
|
||||
|| arg instanceof MultipartFile) {
|
||||
continue;
|
||||
}
|
||||
objList.add(arg);
|
||||
}
|
||||
return JSON.toJSONString(objList);
|
||||
}
|
||||
//得到请求参数名称
|
||||
Signature signature = joinPoint.getSignature();
|
||||
MethodSignature methodSignature = (MethodSignature) signature;
|
||||
String[] parameterNames = methodSignature.getParameterNames();
|
||||
//返回值
|
||||
JSONObject jsonObject = new JSONObject();
|
||||
//循环获取参数值
|
||||
for (int i = 0; i < args.length; i++) {
|
||||
Object arg = args[i];
|
||||
if (arg instanceof HttpServletRequest
|
||||
|| arg instanceof HttpServletResponse
|
||||
|| arg instanceof MultipartFile) {
|
||||
continue;
|
||||
}
|
||||
jsonObject.put(parameterNames[i], arg);
|
||||
}
|
||||
return jsonObject.toJSONString();
|
||||
}
|
||||
}
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
package com.yida.data.log.configure;
|
||||
|
||||
import net.logstash.logback.appender.LogstashTcpSocketAppender;
|
||||
import net.logstash.logback.encoder.LogstashEncoder;
|
||||
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.convert.converter.Converter;
|
||||
import org.springframework.data.convert.ReadingConverter;
|
||||
import org.springframework.data.convert.WritingConverter;
|
||||
import org.springframework.data.elasticsearch.config.ElasticsearchConfigurationSupport;
|
||||
import org.springframework.data.elasticsearch.core.convert.ElasticsearchCustomConversions;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import ch.qos.logback.classic.Logger;
|
||||
import ch.qos.logback.classic.LoggerContext;
|
||||
import cn.hutool.json.JSONUtil;
|
||||
|
||||
@Configuration
|
||||
public class ElasticsearchConfiguration extends ElasticsearchConfigurationSupport {
|
||||
|
||||
@Value("${spring.application.name}")
|
||||
private String applicationName;
|
||||
|
||||
private static final LoggerContext CONTEXT;
|
||||
private static final Logger ROOTLOGGER;
|
||||
|
||||
static {
|
||||
CONTEXT = (LoggerContext) LoggerFactory.getILoggerFactory();
|
||||
ROOTLOGGER = CONTEXT.getLogger("ROOT");
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* conversions for LocalDateTime
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
@Bean
|
||||
@Override
|
||||
public ElasticsearchCustomConversions elasticsearchCustomConversions() {
|
||||
List<Converter> converters = new ArrayList<>();
|
||||
converters.add(DateToLocalDateTimeConverter.INSTANCE);
|
||||
converters.add(LongToLocalDateTimeConverter.INSTANCE);
|
||||
return new ElasticsearchCustomConversions(converters);
|
||||
}
|
||||
|
||||
@Bean
|
||||
public void enableElk() {
|
||||
LogstashTcpSocketAppender appender = new LogstashTcpSocketAppender();
|
||||
LogstashEncoder encoder = new LogstashEncoder();
|
||||
|
||||
HashMap<String, String> customFields = new HashMap<>(2);
|
||||
customFields.put("application-name", applicationName);
|
||||
String customFieldsString = JSONUtil.toJsonStr(customFields);
|
||||
encoder.setCustomFields(customFieldsString);
|
||||
|
||||
appender.setEncoder(encoder);
|
||||
//appender.addDestination(properties.getLogstashHost());
|
||||
appender.setName("logstash[" + applicationName + "]");
|
||||
appender.start();
|
||||
appender.setContext(CONTEXT);
|
||||
ROOTLOGGER.addAppender(appender);
|
||||
}
|
||||
|
||||
@ReadingConverter
|
||||
enum LongToLocalDateTimeConverter implements Converter<Long, LocalDateTime> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public java.time.LocalDateTime convert(Long source) {
|
||||
return Instant.ofEpochMilli(source).atZone(ZoneId.systemDefault()).toLocalDateTime();
|
||||
}
|
||||
}
|
||||
|
||||
@WritingConverter
|
||||
enum DateToLocalDateTimeConverter implements Converter<Date, LocalDateTime> {
|
||||
|
||||
INSTANCE;
|
||||
|
||||
@Override
|
||||
public LocalDateTime convert(Date date) {
|
||||
Instant instant = date.toInstant();
|
||||
return instant.atZone(ZoneId.systemDefault()).toLocalDateTime();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
package com.yida.data.log.service;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.alibaba.fastjson.JSONArray;
|
||||
import com.alibaba.fastjson.JSONObject;
|
||||
import com.alibaba.fastjson.TypeReference;
|
||||
import com.yida.data.common.core.common.ResultBean;
|
||||
import com.yida.data.common.core.entity.CurrentUser;
|
||||
import com.yida.data.common.core.entity.constant.CachePrefixConstant;
|
||||
import com.yida.data.common.core.entity.system.Log;
|
||||
import com.yida.data.common.core.entity.system.enums.OperationLogTypeEnum;
|
||||
import com.yida.data.common.core.utils.FebsUtil;
|
||||
import com.yida.data.log.annotation.OperationLog;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.aspectj.lang.JoinPoint;
|
||||
import org.aspectj.lang.reflect.MethodSignature;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
|
||||
import cc.mrbird.febs.common.redis.service.RedisService;
|
||||
import cn.hutool.core.date.LocalDateTimeUtil;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
/**
|
||||
* 日志service层
|
||||
*
|
||||
* @author ZYJ
|
||||
* @date 2021/1/8
|
||||
*/
|
||||
@Service
|
||||
@Slf4j
|
||||
@RequiredArgsConstructor
|
||||
public class LogService {
|
||||
|
||||
private final RedisService redisService;
|
||||
|
||||
/**
|
||||
* 保存操作日志信息
|
||||
*
|
||||
* @param joinPoint ProceedingJoinPoint类
|
||||
* @param ip ip地址
|
||||
* @param params 请求参数
|
||||
* @param result 接口返回结果
|
||||
* @author ZYJ
|
||||
* @date 2021/1/8 16:30
|
||||
*/
|
||||
@Async
|
||||
public void saveOperationLog(JoinPoint joinPoint, String ip, String params, Object result, Long createId, String createName) {
|
||||
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
|
||||
Method method = signature.getMethod();
|
||||
OperationLog opLog = method.getAnnotation(OperationLog.class);
|
||||
//ip地址
|
||||
Log systemLogInfo = new Log();
|
||||
systemLogInfo.setCreateId(createId);
|
||||
systemLogInfo.setCreateName(createName);
|
||||
// 创建人
|
||||
// if (FebsUtil.isLogin()) {
|
||||
// CurrentUser currentUser = FebsUtil.getCurrentUser();
|
||||
// if (currentUser != null) {
|
||||
// systemLogInfo.setCreateId(currentUser.getUserId());
|
||||
// systemLogInfo.setCreateName(currentUser.getUsername());
|
||||
// }
|
||||
// }
|
||||
systemLogInfo.setIp(ip);
|
||||
//设置开始时间
|
||||
systemLogInfo.setStartTime(LocalDateTime.now());
|
||||
systemLogInfo.setCreateDate(LocalDateTime.now());
|
||||
try {
|
||||
//设置操作模块与操作内容
|
||||
OperationLogTypeEnum opType = opLog.type();
|
||||
systemLogInfo.setModule(opLog.module().getName());
|
||||
systemLogInfo.setMethod(opLog.methods());
|
||||
//设置操作方法名称
|
||||
String className = joinPoint.getTarget().getClass().getName();
|
||||
String methodName = method.getName();
|
||||
methodName = className + "." + methodName;
|
||||
systemLogInfo.setMethodName(methodName);
|
||||
//请求参数
|
||||
systemLogInfo.setOperateParams(params);
|
||||
//返回结果
|
||||
systemLogInfo.setResult(JSON.toJSONString(result));
|
||||
//将返回值转换为对象
|
||||
ResultBean<Object> logResult = JSON.parseObject(JSON.toJSONString(result),
|
||||
new TypeReference<ResultBean<Object>>() {
|
||||
});
|
||||
int successStatus = 200;
|
||||
if (logResult != null) {
|
||||
if (logResult.getStatus() == successStatus) {
|
||||
//设置操作结果
|
||||
systemLogInfo.setResultStatus(logResult.getStatus());
|
||||
systemLogInfo.setResultMsg("操作成功");
|
||||
} else {
|
||||
//设置操作结果
|
||||
systemLogInfo.setResultStatus(logResult.getStatus());
|
||||
systemLogInfo.setResultMsg(logResult.getMessage());
|
||||
}
|
||||
}
|
||||
//日志类型
|
||||
systemLogInfo.setLogType(getLogType(opType.getValue(), params));
|
||||
} catch (Exception e) {
|
||||
log.error("设置操作日志参数失败{}: ", e.getMessage(), e);
|
||||
//设置操作结果
|
||||
systemLogInfo.setResultMsg(e.getMessage());
|
||||
} finally {
|
||||
//设置结束时间
|
||||
systemLogInfo.setEndTime(LocalDateTime.now());
|
||||
//操作时长
|
||||
systemLogInfo.setOperationTime(LocalDateTimeUtil.between(systemLogInfo.getStartTime(),
|
||||
systemLogInfo.getEndTime(), ChronoUnit.MILLIS));
|
||||
//保存数据到redis list数据
|
||||
redisService.lSet(CachePrefixConstant.OPERATION_LOG_LIST, systemLogInfo);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取操作日志类型
|
||||
*
|
||||
* @param value 日志类型枚举值
|
||||
* @param params 请求参数
|
||||
* @return java.lang.String
|
||||
* @author ZYJ
|
||||
* @date 2021/1/11 10:52
|
||||
*/
|
||||
private String getLogType(String value, String params) {
|
||||
|
||||
try {
|
||||
//当为保存类型时
|
||||
if (OperationLogTypeEnum.SAVE.getValue().equals(value)) {
|
||||
//当为保存时, 请求参数被封装成了数组
|
||||
JSONArray jsonArray = JSON.parseArray(params);
|
||||
//获取第一个参数
|
||||
JSONObject jsonObject = jsonArray.getJSONObject(0);
|
||||
if (jsonObject != null) {
|
||||
String id = jsonObject.getString("id");
|
||||
//判断id值
|
||||
if (StringUtils.isNotBlank(id)) {
|
||||
value = OperationLogTypeEnum.UPDATE.getValue();
|
||||
} else {
|
||||
value = OperationLogTypeEnum.INSERT.getValue();
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
} catch (Exception e) {
|
||||
log.error("判断日志类型失败{}: ", e.getMessage(), e);
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
# Auto Configure
|
||||
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
|
||||
com.yida.data.log.aspect.OperationLogAspect,\
|
||||
com.yida.data.log.service.LogService,\
|
||||
com.yida.data.log.configure.ElasticsearchConfiguration
|
||||
@@ -0,0 +1,55 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<configuration scan="true" scanPeriod="60 seconds" debug="false">
|
||||
<springProperty scope="context" name="springAppName" source="spring.application.name"/>
|
||||
<property name="log.path" value="../log/${springAppName}"/>
|
||||
<property name="log.maxHistory" value="15"/>
|
||||
<property name="log.colorPattern"
|
||||
value="%magenta(%d{yyyy-MM-dd HH:mm:ss}) %highlight(%-5level) %boldCyan(${springAppName:-}) %yellow(%thread) %green(%logger) %msg%n"/>
|
||||
<property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss} %-5level ${springAppName:-} %thread %logger %msg%n"/>
|
||||
|
||||
<!--输出到控制台-->
|
||||
<appender name="console" class="ch.qos.logback.core.ConsoleAppender">
|
||||
<encoder>
|
||||
<pattern>${log.colorPattern}</pattern>
|
||||
</encoder>
|
||||
</appender>
|
||||
|
||||
<!--输出到文件-->
|
||||
<appender name="file_info" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>${log.path}/info/info.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
<MaxHistory>${log.maxHistory}</MaxHistory>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<level>INFO</level>
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<appender name="file_error" class="ch.qos.logback.core.rolling.RollingFileAppender">
|
||||
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
|
||||
<fileNamePattern>${log.path}/error/error.%d{yyyy-MM-dd}.log</fileNamePattern>
|
||||
</rollingPolicy>
|
||||
<encoder>
|
||||
<pattern>${log.pattern}</pattern>
|
||||
</encoder>
|
||||
<filter class="ch.qos.logback.classic.filter.LevelFilter">
|
||||
<level>ERROR</level>
|
||||
<onMatch>ACCEPT</onMatch>
|
||||
<onMismatch>DENY</onMismatch>
|
||||
</filter>
|
||||
</appender>
|
||||
|
||||
<root level="debug">
|
||||
<appender-ref ref="console"/>
|
||||
</root>
|
||||
|
||||
<root level="info">
|
||||
<appender-ref ref="file_info"/>
|
||||
<appender-ref ref="file_error"/>
|
||||
</root>
|
||||
</configuration>
|
||||
Reference in New Issue
Block a user